
插件化系统的原理:面向接口编程 遵守开放封闭原则(OCP),系统对扩展开放,对修改封闭
一个插件系统的核心是制定一套消息通信机制,同时将系统运行时的上下文进行封装,按照不同的场景需求暴露给插件。通过消息通信机制将系统和插件隔离,保证插件不会侵入原系统,通过暴露封装后的上下文内容,安全可靠的将系统资源提供给插件调用
使用插件的优点:
- 允许第三方开发者扩展应用功能(webpack的大量插件)
- 拔插式的设计方便开发者添加、删减插件
-
实现一个插件系统

控制插件的加载
- 对插件暴露合适范围的上下文,并对不同场景的上下文做隔离
- 有一套可拔插的消息通信机制,订阅&监听
Hooks 声明
- Hooks 相当于插件的管理中心,每一个生命周期钩子基本对应框架的不同场景
通过不同的 Hook 来对应框架不同的事件、生命周期节点,以便在需要时调用相应的 Hook
// hooks.js,通过这个hook建立一个hash map,相当于一个插件注册中心。每个key代表一个类型的钩子class Hooks {constructor() {this.hooks = new Map();}add(name, fn) {const hooks = this.get(name);hooks.add(fn);this.hooks.set(name, hooks);}get(name) {return this.hooks.get(name) || new Set();}invoke(name, ...args) {for (const hook of this.get(name)) {hook(...args);}}async invokePromise(name, ...args) {for (const hook of this.get(name)) {await hook(...args);}}}module.exports = new Hooks();
Hook 的调用、注册
针对不同场景的具体插件,会注册特定的钩子(大于等于一个钩子)。插件组合使用不同特性的钩子,往其中插入具体业务代码。框架加载插件后,即会自动注册钩子。当运行到指定钩子的时候,相应的也会执行具体插件钩入的业务代码
// index.js#!/usr/bin/env nodeconst fs = require('fs');const hookBus = require('./hooks');function onCreate() {console.log('onCreate');hookBus.invoke('onCreate',{a: 1,b: 2}); // 这里增加了主生命周期钩子的注册,可以将主流程中的上下文变量传过去}async function onStart() {console.log('onStart');await hookBus.invokePromise('onStart', {a: 3, b: 4}); // 这里是一个主生命周期异步钩子的注册}// 这个方法传给plugin,提供给插件来调用钩子function hook(name, fn) {hookBus.add(name, fn);}function loadPlugin() {fs.readdirSync(__dirname).filter(item => /^plugin/.test(item)).forEach(file =>require(require.resolve(`${__dirname}/${file}`)).apply(hook) // 这里统一向钩子暴露了apply方法,作为插件主入口);}function main() {loadPlugin();onCreate();onStart();}main();
// plugin-1.jsconsole.log('plugin-1 loaded');function apply(hook) {hook('onCreate', function(ctx) {console.log('plugin-1 onCreate');console.log(ctx);});hook('onStart', function(ctx) {console.log('plugin-1 onStart');console.log(ctx);});}module.exports = {apply};// plugin-2.jsconsole.log('plugin-2 loaded');function apply(hook) {hook('onCreate', function(ctx) {console.log('plugin-2 onCreate');console.log(ctx);});}module.exports = {apply};
