diff --git a/zh-CN/guide/basic/command.md b/zh-CN/guide/basic/command.md index 3c9047fe03f9..67c0f6c9570e 100644 --- a/zh-CN/guide/basic/command.md +++ b/zh-CN/guide/basic/command.md @@ -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}` diff --git a/zh-CN/guide/develop/publish.md b/zh-CN/guide/develop/publish.md index 678e5137328a..93d84be9f6e5 100644 --- a/zh-CN/guide/develop/publish.md +++ b/zh-CN/guide/develop/publish.md @@ -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` 可以让插件市场中不显示该插件 (通常情况下你不需要这么做) diff --git a/zh-CN/guide/plugin/service.md b/zh-CN/guide/plugin/service.md index cba8b2f62dad..fd6948bf34d0 100644 --- a/zh-CN/guide/plugin/service.md +++ b/zh-CN/guide/plugin/service.md @@ -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' { @@ -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`:服务的名称 (即其在所有上下文中的属性名) @@ -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` 声明依赖的插件也会被停止运行,直到服务再次被实现。这意味着开发者不需要担心服务的生命周期,只需要专注于提供或使用服务的功能即可。 ### 支持热重载 @@ -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() }) @@ -296,7 +279,7 @@ class Console extends Service { ## 在 `package.json` 中声明依赖 {#package-json} -如果你打算将插件发布到插件市场,我们建议在插件的 [`package.json`](../develop/publish.md#koishi-字段) 中对其所提供和使用的服务进行声明。这些字段将显示在控制台中插件的详情页中,帮助使用者更好地理解插件的功能。 +如果你打算将插件发布到插件市场,我们建议在插件的 `package.json` 中对其所提供和使用的服务进行声明。这些字段将显示在控制台中插件的详情页中,帮助使用者更好地理解插件的功能。 ```json title=package.json { @@ -312,9 +295,11 @@ class Console extends Service { 在这里,`required` 对应于必需依赖,`optional` 对应于可选依赖,`implements` 对应于提供的服务。如果你的插件没有使用或提供服务,那么对应的字段可以省略。 -::: tip -这里的声明与上面提到的 `inject` 应当同时存在。 -::: +### 对比 `inject` 声明 {#inject-vs-dep} + + + +对于任何依赖服务的插件,其必须声明 `inject` 才能正常工作,但缺失 `package.json` 中的声明并不会影响插件的运行。尽管如此,我们依然建议你在 `package.json` 中声明依赖,因为这样做能够在安装时提供更多信息,使用者可以一次性地安装插件所需的所有依赖,而不是等到插件运行时才发现缺少了某个服务。 ### 关于 `peerDependencies` {#peer-vs-dep} @@ -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" @@ -373,10 +356,8 @@ export class ExamplePlugin extends DataService { ```json { - "koishi": { - "service": { - "required": ["console"] - } + "service": { + "required": ["console"] }, "peerDependencies": { "@koishijs/plugin-console": "^5.13.0"