乐趣区

关于javascript:怎样设计一个-JavaScript-插件系统

作者:Bryan Braun

翻译:疯狂的技术宅

原文:https://css-tricks.com/design…

未经容许严禁转载

不论是 jQuery、Vue 还是 React,它们都反对插件。

插件是库和框架中很常见的性能,并且有一个充沛的理由:它们容许开发人员以平安、可扩大的形式增加新的性能。这可能使你的我的项目具备更高的价值,而且也不会减少太多额定的保护累赘。

那么该如何构建插件零碎呢?在本文中咱们用 JavaScript 构建一个本人的插件零碎。

在这里我用的是 插件(plugin)这个词,有时也被称做是 扩大(extensions),附加组件(add-ons)或 模块(modules)。无论叫什么名字,概念都是一样的。

构建一个插件零碎

先从一个简略的 JavaScript 计算器我的项目 betaCalc 开始,其他人能够为它增加新的性能。上面是根本的代码:

// 计算器我的项目
const betaCalc = {
  currentValue: 0,
  
  setValue(newValue) {
    this.currentValue = newValue;
    console.log(this.currentValue);
  },
  
  plus(addend) {this.setValue(this.currentValue + addend);
  },
  
  minus(subtrahend) {this.setValue(this.currentValue - subtrahend);
  }
};

// 应用计算器
betaCalc.setValue(3); // => 3
betaCalc.plus(3);     // => 6
betaCalc.minus(2);    // => 4

咱们先把计算器定义为一种客观事物,这样能够使事件变得简略。计算器的工作原理是通过 console.log 把后果输入到控制台。

目前性能很无限。在代码中有一个 setValue 办法,可能承受一个数字并将其显示在“屏幕”上。还有 plusminus 办法,它们对以后显示的值执行对应的操作。

接下来就要增加更多的性能了。首先创立一个插件零碎:

世界上最小的插件零碎

先创立一个 register 办法,这样其他人就能够向 BetaCalc 注册插件了。它要做的工作很简略:失去一个内部插件,获取它的 exec 函数,并将其作为新办法附加到计算器上:

// 计算器我的项目
const betaCalc = {
  // 计算器相干的其余代码

  register(plugin) {const { name, exec} = plugin;
    this[name] = exec;
  }
};

上面的例子为计算器提供了一个计算平方的 squared 函数:

// 定义插件
const squaredPlugin = {
  name: 'squared',
  exec: function() {this.setValue(this.currentValue * this.currentValue)
  }
};

// 注册插件
betaCalc.register(squaredPlugin);

在大多数插件零碎中,插件通常被分为两个局部:

  1. 要被执行的代码
  2. 元数据(包含名称、形容、版本号、依赖项等)

在下面定义的插件中,exec 函数中蕴含咱们的代码,而 name 是元数据。在注册插件之后,exec 函数将会作为一种办法间接附加到咱们的 betaCalc 对象上,从而使其能够拜访 BetaCalc 的 this

当初 BetaCalc 多了一个计算平方的 squared 函数,能够间接调用:

betaCalc.setValue(3); // => 3
betaCalc.plus(2);     // => 5
betaCalc.squared();   // => 25
betaCalc.squared();   // => 625

这个零碎的长处很多。该插件是一种简略的对象字面量,能够传递给咱们的函数。这意味着能够通过 npm 去下载插件并将其作为 ES6 模块导入。轻松散发是十分重要的。

不过这个插件零碎有一些缺点。

通过为插件提供对 BetaCalc 的 this 的拜访权限,他们能够对所有 BetaCalc 的代码进行读写访问。尽管这样对于获取和设置 currentValue 很轻松,然而也很危险。如果一个插件要从新定义一个外部函数(如 setValue),它会给 BetaCalc 和其余插件带来意想不到的结果。这违反了 凋谢关闭准则,凋谢关闭准则的核心思想是软件实体是可扩大而不可批改的。

另外 squared 函数是通过产生副作用) 发挥作用的。这在 JavaScript 中很常见,然而感觉并不好,特地是当其余插件可能处在同一外部状态的状况下。咱们须要一种更加实用的办法使咱们的零碎更平安、更可预测。

更好的插件架构

让咱们换一种更好的插件架构。上面的代码更改了计算器及其插件 API:

// 计算器我的项目
const betaCalc = {
  currentValue: 0,
  
  setValue(value) {
    this.currentValue = value;
    console.log(this.currentValue);
  },
 
  core: {'plus': (currentVal, addend) => currentVal + addend,
    'minus': (currentVal, subtrahend) => currentVal - subtrahend
  },

  plugins: {},    

  press(buttonName, newVal) {const func = this.core[buttonName] || this.plugins[buttonName];
    this.setValue(func(this.currentValue, newVal));
  },

  register(plugin) {const { name, exec} = plugin;
    this.plugins[name] = exec;
  }
};
  
// 咱们的插件
const squaredPlugin = { 
  name: 'squared',
  exec: function(currentValue) {return currentValue * currentValue;}
};

betaCalc.register(squaredPlugin);

// 应用计算器
betaCalc.setValue(3);      // => 3
betaCalc.press('plus', 2); // => 5
betaCalc.press('squared'); // => 25
betaCalc.press('squared'); // => 625

咱们在代码中做了如下批改:

首先把插件与计算器的“外围”办法(如 plusminus)离开,做法是将其放入它本人的插件对象中。将咱们的插件存储在一个 plugin 对象中能够使零碎更加平安。当初此插件拜访 this 时看不到 BetaCalc 的属性,只能失去 betaCalc.plugins 属性。

其次,咱们实现了一个 press 办法,该办法按名称查找性能对应的函数,而后调用。当初,当咱们调用插件的exec 函数时,会把计算器以后的值(currentValue)传给它,并失去新的值。

从实质上来说,新减少的 press 办法把所有的计算器性能都转换为了纯函数(pure functions),它们返回后果只依赖其参数,并且在执行过程中没有副作用。这样做有很多益处:

  • 简化了 API。
  • 简化了测试(对于 BetaCalc 和插件自身)。
  • 它缩小了零碎的依赖性,也就是实现了松耦合。

这种新的架构与第一个例子相比受到了更多的限度,然而成果很好。实际上,咱们为插件的作者设置了防护边界,限度他们只能做咱们容许的事。

不过它可能过于严格了,当初咱们的计算器插件只能对 currentValue 进行操作。如果插件的作者想要增加一些高级的性能,例如“暂存后果”或跟踪历史记录的性能,就无能为力了。

从另外一个角度来看,兴许没什么关系。因为你赋予插件作者的力量是一种奥妙的均衡。给他们凋谢过多的性能由可能会影响我的项目的稳定性,然而反过来,给他们的性能过少会也使他们很难解决本人的问题,如果这样的话你还不如没有插件。

还须要做些什么

为了改善咱们零碎,还须要做很多工作。

如果插件作者忘了定义名称或返回值,能够通过增加错误处理机制来告诉插件作者。须要像 QA 那样思考问题,并设想在什么状况下会使咱们的零碎解体,这样能力使咱们为这些状况增加容错机制并防止解体。

咱们还能够扩大插件的性能范畴。当初一个 BetaCalc 插件能够增加一个性能。不过如果它还能够为某些生命周期事件注册回调,例如计算器将要显示后果值时,该怎么办?或者,如果有一个专用的地位来存储多个交互中的状态该怎么办?

咱们还能够扩大插件注册。如果须要应用一些初始设置来注册插件怎么办?能够使插件更灵便吗?如果插件作者心愿注册整个性能套件而不是一个性能该怎么办?为了反对这一点须要做哪些更改?

你本人的插件零碎

BetaCalc 及其插件零碎都非常简单。如果你的我的项目比拟大,那就须要对其余的插件架构做一些摸索。

一个很好的路径是参考现有胜利我的项目的插件零碎。对于 JavaScript 我的项目来说,你能够参考 jQuery、D3、CKEditor 等。

你还须要相熟各种 JavaScript 设计模式。Addy Osmani 的《javascript 设计模式》这本书就挺不错的。每种设计模式都提供了不同的接口和耦合度,你能够为本人的插件零碎筛选适合的体系结构。理解这些能够帮你更好地均衡每个人的需要。

除了设计模式自身之外,还能够借鉴许多好的软件开发准则来做出这类决策。除了我在后面提到过的一些办法(例如:开闭原理和松耦合)之外,还包含一些其余的办法,例如 Demeter 法令和 dependency 注入。

看上去须要理解的常识有很多,然而你必须钻研它们。让每个人都重写他们的插件是最苦楚的一件事,因为你须要扭转插件的架构。这会让别人失去对你的信赖,并且会阻止他们在将来为你的零碎做出奉献。

总结

从零开始写一个好的插件架构是十分艰难的,你必须思考并衡量很多因素来构建满足所有人的需要。它足够简略吗?足够弱小吗?能够长期工作吗?

这种致力的付出是值得的,领有一个好的插件零碎能够帮忙所有人。开发人员能够自在的解决问题,最终用户能够领有大量的抉择,这样你就能够在围绕本人的我的项目倒退生态系统和社区。这是一种双赢的场面。


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章

欢送持续浏览本专栏其它高赞文章:

  • 深刻了解 Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13 个帮你进步开发效率的古代 CSS 框架
  • 疾速上手 BootstrapVue
  • JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
  • WebSocket 实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30 分钟用 Node.js 构建一个 API 服务器
  • Javascript 的对象拷贝
  • 程序员 30 岁前月薪达不到 30K,该何去何从
  • 14 个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把 HTML 转成 PDF 的 4 个计划及实现

  • 更多文章 …
退出移动版