Skip to content

事件系统

字数
1169 字
阅读时间
5 分钟

在上一节中,我们已经了解了插件的基本结构和注册方式。接下来,我们将深入了解 IPE 的事件系统。

事件系统在 IPE 中扮演着底层的角色,事件系统允许插件之间进行通信和协作。它支持监听运行状态的生命周期事件和提供扩展性的自定义事件。

基本用法

让我们先从一个基本示例开始:

ts
.('quickEdit/wikiPage', () => {
  const {  } = 
  .('当前正在编辑的页面是:', .)
})

上述代码片段实现了一个简单的功能:当“快速编辑”插件加载了页面数据后,控制台会输出当前正在编辑的页面标题。

如你所见,ctx.on() 方法监听了一个事件。传入的第一个参数 quickEdit/wikiPage 是事件的名称,而第二个参数则是事件的回调函数。每一次 quickEdit/wikiPage 事件被触发 (即用户进入快速编辑模式,页面数据加载完成) 时都会调用该函数。

事件与会话构成了最基础的交互模型。你在监听器中不仅能够打印控制台消息,还能够像写普通网页脚本一样处理更复杂的事务,例如:

ts
ctx.plugin({
  name: 'wiki-editor',
  apply(ctx) {
    ctx.on('quickEdit/wikiPage', async (payload) => {
      const { wikiPage, modal } = payload
      const $content = modal.get$content()
      if (wikiPage.title.includes('兽耳娘')) {
        $content.prepend('好好好!')
      }
    })
  },
})

监听事件

聪明的你应该不难发现,这一套 API 非常像 Node.js 的 EventEmitter:第一个参数表示要监听的事件名称,第二个参数表示事件的回调函数。

这套事件系统与 EventEmitter 的一个不同点在于,无论是 ctx.on() 还是 ctx.once() 都会返回一个 dispose 函数,调用这个函数即可取消注册监听器。因此你其实不必使用 ctx.once()ctx.off()。下面给一个只触发一次的监听器的例子:

ts
const  = .('foo', () => {
  .('只会触发一次')
  () // 取消注册
})

事件的命名

IPE 的事件名字可以是任意字符串,不过官方插件的事件名称一般遵循 插件ID/事件名称 的命名规范。

我们也推荐这样做,因为这样可以避免事件名称冲突,并且让事件的来源一目了然。

触发事件

如果你开发的插件希望允许其他插件扩展,那么触发事件就是相当合适的手段。

触发事件的基本用法也都与 EventEmitter 类似,第一个参数是事件名称,之后的参数对应回调函数的参数。下面是一个例子:

ts
.('custom-event', arg1, arg2, ...rest)
Cannot find name 'rest'.
Cannot find name 'arg2'.
Cannot find name 'arg1'.
// 对应于 .('custom-event', (, , ...) => {})

触发方式

IPE 的事件系统与 EventEmitter 的另一个区别在于,触发一个事件可以有着多种形式,目前支持 4 个不同的方法,足以适应绝大多数需求。

  • emit: 同时触发所有 event 事件的回调函数
  • parallel: 上述方法对应的异步版本
  • bail: 依次触发所有 event 事件的回调函数;当返回一个 false, null, undefined 以外的值时将这个值作为结果返回
  • serial: 上述方法对应的异步版本

声明自定义事件

当然,定义事件的类型不是必须的,如果你只是想要开发一些简单的自用插件,完全不必如此大费周章。除非你有强迫症。

对于 TypeScript 开发者来说,你可以通过声明模块的方式,为事件添加类型定义:

ts
interface SomeEventPayload {
  : string
  : number
}

declare module '@inpageedit/core' {
  interface  {
    'myPlugin/someEvent'(: SomeEventPayload): void
  }
}

.('myPlugin/someEvent', () => {
  .(.)
})

这样其它开发者即使不去看文档或者读源码,也能通过类型推导,对事件的参数有一个大概的认识。


再比如,你想要监听另一个插件提供的事件,请记得在文件头部导入类型声明:

ts
import type {} from 'inpageedit-plugin-foo' // 引入类型定义

// 如果没有上面的类型导入,下面的代码爆类型错误
ctx.on('foo/someEvent', (payload) => {})

贡献者

The avatar of contributor named as dragon-fish dragon-fish

✏️ InPageEdit NEXT