Skip to content

第一个插件

字数
1301 字
阅读时间
6 分钟

认识插件:函数式插件

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

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

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

TIP

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

ts
ipe
.
plugin
((
ctx
) => {
console
.
log
(
ctx
, 'hello, world')
})

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

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

让我们做些什么

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

ts
ipe
.
plugin
((
ctx
) => {
ctx
.
toolbox
.
addButton
({
id
: 'my-first-plugin',
icon
: '👋',
tooltip
: 'My First Plugin',
onClick
: () => {
alert
('hello, world')
}, }) })

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

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

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

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

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

依赖注入

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

ts
ipe
.
plugin
((
ctx
) => {
ctx
.
inject
(['toolbox'], (
ctx
) => {
ctx
.
toolbox
.
addButton
({
id
: 'my-first-plugin',
icon
: '👋',
tooltip
: 'My First Plugin',
onClick
: () => {
alert
('hello, world')
}, }) }) })

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

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

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

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

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

ts
ipe
.
plugin
({
inject
: ['toolbox'],
apply
(
ctx
) {
ctx
.
toolbox
.
addButton
({
id
: 'my-first-plugin',
icon
: '👋',
tooltip
: 'My First Plugin',
onClick
: () => {
alert
('hello, world')
}, }) }, })

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

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

可选的属性:

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

OOP 爱好者:类式插件

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

ts
import { 
InPageEdit
} from '@inpageedit/core'
class
MyButtonPlugin
{
static
inject
= ['toolbox']
constructor( public
ctx
:
InPageEdit
,
config
?: any
) {
ctx
.
toolbox
.
addButton
({
id
: 'my-first-plugin',
icon
: '👋',
tooltip
: 'My First Plugin',
onClick
: () => {
alert
('hello, world')
}, }) } }
ipe
.
plugin
(
MyButtonPlugin
)

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

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

ts
import { 
InPageEdit
,
BasePlugin
,
Inject
} from '@inpageedit/core'
import type {} from '@inpageedit/core/plugins/toolbox/index' @
Inject
(['toolbox'])
export default class
MyButtonPlugin
extends
BasePlugin
{
constructor( public
ctx
:
InPageEdit
,
config
?: any
) { super(
ctx
,
config
, 'MyButtonPlugin')
}
start
() {
this.
ctx
.
toolbox
.
addButton
({
id
: 'my-first-plugin',
icon
: '👋',
tooltip
: 'My First Plugin',
onClick
: () => {
alert
('hello, world')
}, }) }
stop
() {
this.
ctx
.
toolbox
.
removeButton
('my-first-plugin')
} }
ts
import 
PluginMyButton
from './PluginMyButton.js'
ipe
.
plugin
(
PluginMyButton
)

总结:插件的基本形式

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

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

贡献者

暂无相关贡献者

✏️ InPageEdit NEXT