Skip to content

Commit

Permalink
update service, close #173
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jun 4, 2024
1 parent f2b6ce0 commit 99d47db
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 53 deletions.
1 change: 1 addition & 0 deletions zh-CN/guide/basic/command.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ ctx.command('test [arg:number]')

- string: `string` 字符串
- number: `number` 数值
- bigint: `bigint` [大整数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
- text: `string` 贪婪匹配的字符串
- user: `string` 用户,格式为 `{platform}:{id}`
- channel: `string` 频道,格式为 `{platform}:{id}`
Expand Down
5 changes: 1 addition & 4 deletions zh-CN/guide/develop/publish.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,12 @@ root
"optional": ["assets"], // 可选的服务
"implements": ["dialogue"], // 实现的服务
},
"locales": ["en", "zh"], // 支持的语言
}
}
```

- **description:** 插件描述,应该是一个对象,其中的键代表语言名,值是对应语言下的描述
- **service:** 插件的服务相关信息,具体包含下列属性:
- **implements:** 实现的服务,应该是一个服务名构成的数组
- **locales:** 插件支持的语言,应该是一个语言名构成的数组
- **service:** 插件的服务相关信息,详情请参见 [服务与依赖](../plugin/service.html#package-json)
- **preview:** 配置为 `true` 可以让插件显示为「开发中」状态
- **hidden:** 配置为 `true` 可以让插件市场中不显示该插件 (通常情况下你不需要这么做)

Expand Down
79 changes: 30 additions & 49 deletions zh-CN/guide/plugin/service.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,19 +202,16 @@ plugins:
如果你希望自己插件提供一些接口供其他插件使用,那么最好的办法便是提供自定义服务,就像这样:

```ts
import Console from '@koishijs/plugin-console'
// ---cut---
// 这个表达式定义了一个名为 console 的服务
Context.service('console')

// 假如你在某个上下文设置了这个值,其他的上下文也将拥有此属性
app.guild().console = new Console()
app.private().console instanceof Console // true
export default class Console extends Service {
constructor(ctx: Context) {
super(ctx, 'console')
}
}
```

这种做法的本质原理是修改了 Context 的原型链
这样定义的好处在于,`Console` 本身也是一个合法的插件,其他插件可以直接通过 `ctx.plugin(Console)` 来加载它

对于 TypeScript 用户,你还需要进行声明合并,以便能够在上下文对象中获得类型提示:
对于 TypeScript 用户,在定义服务时,你还需要进行声明合并,以便能够在上下文对象中获得类型提示:

```ts no-extra-header
declare module 'koishi' {
Expand All @@ -224,23 +221,21 @@ declare module 'koishi' {
}
```

### 服务的生命周期

相比直接赋值,我们更推荐你从 Service 派生子类来实现自定义服务:
而使用服务的插件则需要声明依赖并导入类型:

```ts
class Console extends Service {
constructor(ctx: Context) {
// 这样写你就不需要手动给 ctx 赋值了
super(ctx, 'console', true)
}
}
import {} from 'koishi-plugin-console'

export const inject = ['console']

// 这样定义的好处在于,Console 本身也是一个插件
app.plugin(Console)
export function apply(ctx: Context) {
ctx.console.addEntry('/path/to/dialogue/extension')
}
```

Service 抽象类的构造函数支持三个参数:
### 服务的生命周期

`Service` 抽象类的构造函数支持三个参数:

- `ctx`:服务所在的上下文对象
- `name`:服务的名称 (即其在所有上下文中的属性名)
Expand All @@ -254,19 +249,7 @@ Service 抽象类的构造函数支持三个参数:

默认情况下,一个自定义服务会先等待 ready 事件触发,然后调用可能存在的 `start()` 方法,最后才会被注册到全体上下文中。这种设计确保了服务在能够被访问的时候就已经是可用的。但如果你的服务不需要等待 ready 事件,那么只需传入第三个参数 `true` 就可以立即将服务注册到所有上下文中。

此外,当注册了服务的插件被卸载时,其注册的服务也会被移除,其他插件不再可以访问到这项服务:

```ts
import Console from '@koishijs/plugin-console'
// ---cut---
app.console // falsy
app.plugin(Console) // 加载插件
app.console // truthy
app.dispose(Console) // 卸载插件
app.console // falsy
app.plugin(Console) // 重新加载插件
app.console // truthy
```
此外,当注册了服务的插件被卸载时,其注册的服务也会被移除,通过 `inject` 声明依赖的插件也会被停止运行,直到服务再次被实现。这意味着开发者不需要担心服务的生命周期,只需要专注于提供或使用服务的功能即可。

### 支持热重载

Expand All @@ -284,9 +267,9 @@ class Console extends Service {
this.entries.add(filename)
this.triggerReload()

// 注意这个地方,caller 属性会指向访问此方法的上下文
// 注意这个地方,ctx 属性会指向访问此方法的上下文 (而不是构造函数中的上下文)
// 只需要在这个上下文上监听 dispose 事件,就可以顺利处理副作用了
this[Context.current]?.on('dispose', () => {
this.ctx.on('dispose', () => {
this.entries.delete(filename)
this.triggerReload()
})
Expand All @@ -296,7 +279,7 @@ class Console extends Service {

## `package.json` 中声明依赖 {#package-json}

如果你打算将插件发布到插件市场,我们建议在插件的 [`package.json`](../develop/publish.md#koishi-字段) 中对其所提供和使用的服务进行声明。这些字段将显示在控制台中插件的详情页中,帮助使用者更好地理解插件的功能。
如果你打算将插件发布到插件市场,我们建议在插件的 `package.json` 中对其所提供和使用的服务进行声明。这些字段将显示在控制台中插件的详情页中,帮助使用者更好地理解插件的功能。

```json title=package.json
{
Expand All @@ -312,9 +295,11 @@ class Console extends Service {

在这里,`required` 对应于必需依赖,`optional` 对应于可选依赖,`implements` 对应于提供的服务。如果你的插件没有使用或提供服务,那么对应的字段可以省略。

::: tip
这里的声明与上面提到的 `inject` 应当同时存在。
:::
### 对比 `inject` 声明 {#inject-vs-dep}

<!-- 按理说,我已经在 `inject` 中声明过一次服务依赖了,为什么还要在 `package.json` 中再声明一次呢? -->

对于任何依赖服务的插件,其必须声明 `inject` 才能正常工作,但缺失 `package.json` 中的声明并不会影响插件的运行。尽管如此,我们依然建议你在 `package.json` 中声明依赖,因为这样做能够在安装时提供更多信息,使用者可以一次性地安装插件所需的所有依赖,而不是等到插件运行时才发现缺少了某个服务。

### 关于 `peerDependencies` {#peer-vs-dep}

Expand Down Expand Up @@ -346,10 +331,8 @@ export function apply(ctx: Context) {

```json title=package.json
{
"koishi": {
"service": {
"required": ["puppeteer"]
}
"service": {
"required": ["puppeteer"]
},
"devDependencies": {
"koishi-plugin-puppeteer": "^2.0.0"
Expand All @@ -373,10 +356,8 @@ export class ExamplePlugin extends DataService {

```json
{
"koishi": {
"service": {
"required": ["console"]
}
"service": {
"required": ["console"]
},
"peerDependencies": {
"@koishijs/plugin-console": "^5.13.0"
Expand Down

0 comments on commit 99d47db

Please sign in to comment.