Skip to content

第一个插件

字数
1301 字
阅读时间
6 分钟

认识插件:函数式插件

最简单的 IPE 的插件,就是一个接受依赖注入的普通函数。

js
/**
 * 我们的第一个插件!
 * @param {import('@inpageedit/core').InPageEdit} ctx - 插件所属的上下文
 * @param {any} [config] - 可能传入的配置项
 */
function (, ) {
  .(, `hello, ${.name || 'world'}`) // hello, IPE
}
// 然后加载它
.(, { : 'IPE' })

你没看错,就这么一个简单的函数,便是合法的 IPE 插件。它的第一个参数 ctx 是插件所属的上下文,你也可以接受第二个参数 config,它是可能传入的配置项。

TIP

对于简单的插件来说,我们更推荐直接传入一个箭头函数,除了了更简洁之外,还能自动获得关于 ctx 的类型推导!

ts
.(() => {
  .(, 'hello, world')
})

你可以亲自尝试一下:打开你的浏览器控制台(F12),粘贴上面的代码,按下回车键。

就是这么简单,当你加载这个插件后,立刻就能在控制台看到 hello, world 字样。

让我们做些什么

为了更好的理解 IPE 的插件机制,我们让这个插件做点事情:

ts
.(() => {
  ..({
    : 'my-first-plugin',
    : '👋',
    : 'My First Plugin',
    : () => {
      ('hello, world')
    },
  })
})

看看页面右下角的工具盒,是不是多了一个按钮?点击它,你会看到一个弹窗,上面写着 hello, world

真不赖,我们的插件看上去离“快速编辑”也不远嘛!

但是控制台中有一些警告:

Error: property toolbox is not registered, declare it as inject to suppress this warning

别紧张,这是因为我们没有在插件中注入 toolbox 服务。

依赖注入

为了解决这个问题,我们需要在插件中注入 toolbox 服务。

ts
.(() => {
  .(['toolbox'], () => {
    ..({
      : 'my-first-plugin',
      : '👋',
      : 'My First Plugin',
      : () => {
        ('hello, world')
      },
    })
  }) 
})

现在没有报错了!但是,为什么要这样做?

因为 ctx.toolbox 其实是由另外一个插件提供的服务,它的加载时机是不确定的。人总有运气差的时候,万一咱们的myButtonPlugin安装时toolbox还没有加载,那你就只能望 undefined 而兴叹了。

而通过 ctx.inject,IPE会确保你在插件中拿到的上下文,一定包含了 toolbox 服务。

更清晰的定义归纳:对象式插件

不过总是ctx.inject有时候会导致难以阅读,所以我们可以换用对象式插件:

ts
.({
  : ['toolbox'],
  () {
    ..({
      : 'my-first-plugin',
      : '👋',
      : 'My First Plugin',
      : () => {
        ('hello, world')
      },
    })
  },
})

现在你学会了第二种插件的写法:IPE 插件可以是一个包含 apply 方法的对象。

  • apply 完全等同于函数式插件中的函数

可选的属性:

  • name 属性来传递插件的名称。
  • inject 属性来传递服务列表。

OOP 爱好者:类式插件

IPE的插件可以是一个普通的类,它的 constructor 的第一个参数是所在的上下文,第二个参数是可能传入的配置项。

ts
import {  } from '@inpageedit/core'

class  {
  static  = ['toolbox']
  constructor(
    public : ,
    ?: any
  ) {
    ..({
      : 'my-first-plugin',
      : '👋',
      : 'My First Plugin',
      : () => {
        ('hello, world')
      },
    })
  }
}

.()

一般来说,TypeScript 的爱好者会很喜欢这种写法,因为它更符合面向对象编程的习惯,你可以把插件的状态保存在类的实例中,也可以把一些逻辑归纳在类的方法中。

如果你感兴趣的话,你甚至可以继承 BasePlugin 来简化一些样板代码,或者使用修饰器来声明依赖注入之类的:

ts
import { , ,  } from '@inpageedit/core'
import type {} from '@inpageedit/core/plugins/toolbox/index'

@(['toolbox'])
export default class  extends  {
  constructor(
    public : ,
    ?: any
  ) {
    super(, , 'MyButtonPlugin')
  }

  () {
    this...({
      : 'my-first-plugin',
      : '👋',
      : 'My First Plugin',
      : () => {
        ('hello, world')
      },
    })
  }

  () {
    this...('my-first-plugin')
  }
}
ts
import  from './PluginMyButton.js'

.()

总结:插件的基本形式

一个插件需要是以下三种基本形式之一:

  1. 一个接受两个参数的函数,第一个参数是插件所属的上下文,第二个参数是可能传入的配置项
  2. 一个对象,其中的 apply 方法是第一种形式中所说的函数,包括可选的 injectname 等属性
  3. 一个接受两个参数的类,constructor 的第一个参数是插件所属的上下文,第二个参数是可能传入的配置项
ts
.((, ) => {
  // ...
})

.({
  : [],
  : (, ) => {
    // ...
  },
})

.(
  class  {
    constructor(
      public : ,
      public ?: any
    ) {
      // ...
    }
  }
)

贡献者

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

✏️ InPageEdit NEXT