【译】设计一个 JS 插件系统

avatar
@智云健康

原文地址css-tricks.com/designing-a…

译者:Gavin,未经授权禁止转载。

前言

许多开源库都有插件系统,例如:VuejQueryGatsby。插件是库和框架的一个常见特性,用于允许开发人员以一种安全、可伸缩的方式添加功能。那么怎么构建一个插件系统呢?让我们用JS构建一个自己的插件系统来回答这个问题。

简单的计算器示例

从一个名为BetaCalc的示例项目开始。BetaCalc的目标是成为一个简单的JS计算器,其他开发者可以为其添加”按钮“。代码如下:

我们将计算器定义为简单的对象字面量,计算结果将使用console.log打印。现在功能很简单,有一个setValue方法,用于模拟计算器上的数字输入,其中的console.log模拟计算器上的显示屏,还有两个方法,用于对应计算器中的加号按钮与减号按钮。

为计算器程序添加插件系统

建立插件系统第一步就是要建立一个插件注册方法,其他开发人员可以利用其注册插件。这个方法工作很简单:获取外部插件,抓取其exec函数,并将它作为一个新的方法(“按钮”)附加到计算器上:

这里有一个插件的例子,它给计算器增加“平方”按钮:

在许多插件系统中,插件通常由两部分组成:

  1. 需要执行的代码;
  2. 元数据(如:名称、描述、版本号、依赖关系等)

在我们的插件中,exec函数包含了需要执行的代码,函数名称为元数据。当插件注册后,exec函数作为方法直接附加到我们的betaCalc对象上,使它能够访问betaCalc的this。现在BetaCalc有了一个新的“平方”按钮,可以直接调用:

这个系统有很多不错的地方,它是一个简单的对象字面量,可以传递到函数中。意味着插件可以通过npm下载,并作为ES6模块导入,可以实现简单的分发。

但现在系统还存在一些缺陷:

通过允许插件访问BetaCalcthis,插件可以读/写所有BetaCalc中的代码,虽然这对于获取和设置currentValue很有用,但是也很危险。如果一个插件要重新定义一个内部函数(如:setValue),它可能会为BetaCalc和其它插件产生意想不到的结果。这违反了开闭原则,该原则规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”。

此外,“平方”函数的工作原理会产生“副作用”。这在JS中并不罕见,但感觉并不好--尤其是当其它插件可能处于同样的内部状态时。一个更加实用的方法将大大有助于我们的系统更加安全和可预测。

一个更好的插件系统架构

再来看一个更好的插件架构,下面例子改变了计算器和它的插件API:

这里有一些显著的变化。

首先,我们将插件系统与“核心”计算器方法(如“加号”与“减号”)分离开,将它们放入自己的插件对象中,使系统更加安全。现在插件将不能直接读写BetaCalc的属性--它们只能看到BetaCalc.plugins的属性。

其次,我们实现了一个press方法,它根据名称查找功能(“按钮”)函数,然后调用它。现在,当我们调用一个插件的exec函数时,我们将当前计算器的值currentValue传递给它,并期望它返回新的计算器值。

本质上,这种新的press方法转换成纯功能的计算器“按钮”:获取当前计算器值,执行操作,返回结果。这有很多好处:

  • 简化了API
  • 使测试更加容易(对于BetaCalc和插件本身)
  • 减少了我们系统的依赖性,使它更松耦合

这个新的架构比第一个示例有更多的局限性,但显然第二种方式更好,能限制插件只做我们想让它做的改变。

现在我们的计算器插件只能对currentValue进行操作。如果一个插件作者想要添加一些高级的功能,比如"内存"按钮或者一种追踪历史记录的方法,他们可能做不到。这里需要权衡:给插件作者太多权利可能会影响项目的稳定性,但太少的权利可能使他们很难解决他们的问题。

还能做什么

还可以做很多事情来改进我们的系统。例:

  1. 如果插件作者忘记定义名称或返回值,我们可以添加错误处理来通知他们;
  2. 扩展插件的范围。目前BetaCalc插件仅可以添加一个"按钮",我们可以为某些生命周期事件注册回调;
  3. 扩展插件注册机制,目前BetaCalc仅支持一次注册一个"按钮",我们可以使其支持批量注册。

设计你的插件系统

如果你的项目比较大,那么可以参考一些开源的插件架构。例如jQueryGatsbyD3CKEditor或者其它。 当然你也可以熟悉JS的设计模式,或者阅读Addy Osmani的《学习JS设计模式》,这为你提供了许多优秀的插件架构选项。了解这些选项可以帮助你更好地平衡使用你项目所有人的需求。

除了设计模式外,你还可以利用优秀的软件开发原则来做出这类决策。如前面提到的:开闭原则松耦合,还有一些其它相关的原则,如:得墨忒耳定律依赖注入等。

看起来很多,但作为一个插件系统设计者,你必须得研究,因为没有什么比让每个人都重写他们的插件更痛苦。当你改变你开源项目的插件架构时,将使你的开源项目失去信任,会大大降低开发者为你的项目作出贡献的热度。

总结

从头开始编写一个好的插件系统架构是很困难的,你必须平衡考虑许多因素,以构建一个满足每个人需求的系统。系统足够简单吗?足够强大吗?能长期发挥作用吗?

不过这是值得的。拥有一个好的插件系统对每个人都有帮助。开发者可以自由地解决他们的问题,终端用户可以从大量的插件中进行选择。你可以围绕你的项目建立一个生态系统和社区。这是一个双赢的局面。

📣智云健康急招聘

欢迎小伙伴来和我们一起来做分享、做开源呀😃~