【本文主旨】仅探讨插件系统的实现,不针对某个具体功能的插件。
【关键词】依赖与被依赖者的管理、赋能、vue、webpack、babelhtml
对插件最直观的认识,顾名思义,即插即用,具备很高的灵活性;另外插件彻底解耦于使用者,只要知足对接口的约定便可。 本文主要从目前使用较多的几个工具入手,简单介绍下插件的使用,以及它们对插件的约定格式。vue
前言
从简单的例子开始:webpack
function pluginA(App) {
App.protoType.a = 1;
}
function pluginB(App) {
App.protoType.b = 1;
}
class App { }
//对App原型进行加强
pluginA(App);
pluginB(App);
复制代码
这种写法,从语义上看,plugin是主动施与者,为了统一对外接口,较多的写法为:git
class App {
use(plugins) {
let type = Object.protoType.toString.call(plugins);
if(type === '[object Function]') {
plugins.call(null, this);
}else if(type === '[object Array]') {
plugins.forEach(plugin => {
pulgin.call(null, this);
});
}
}
}
//用法一
App.use(pluginA);
App.use(pluginB);
//用法二
App.use([pluginA, pluginB]);
复制代码
由使用者提供接口,语义上更友好。App:"我须要pluginA"。pluginA便为之所用。是否是有些IOC的意思?
github
webpack插件
插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者能够引入它们本身的行为到 webpack 构建流程中。web
插件写法编程
function MyPlugin(options) {
};
// 在插件函数的 prototype 上定义一个 `apply` 方法。
MyPlugin.prototype.apply = function(compiler) {
// 指定一个挂载到 webpack 自身的事件钩子。
compiler.plugin('webpacksEventHook', function(compilation, callback) {
// 功能完成后调用 webpack 提供的回调。
callback();
});
};
module.exports = MyPlugin;
复制代码
顺便一提:
compiler:
表明的是不变的webpack环境,是针对webpack的;
compilation:
针对的是随时可变的项目文件,只要文件有改动,compilation就会被从新建立。redux
用法bash
var MyPlugin = require('my-plugin');
var webpackConfig = {
...,
plugins: [
new MyPlugin({options: true})
]
};
复制代码
深刻理解babel
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
//调用plugin的apply方法
plugin.apply(compiler);
}
}
复制代码
由此看出,webpack内部对插件的统一处理:获取插件的apply方法,将compiler注入,以完成事件钩子的注册。此处apply即是webpack处理插件的通用接口,所以编写webpack插件,必须提供apply方法。Vue插件
//1. 若插件为对象,必须提供install方法
MyPlugin.install = function (Vue, options) { }
export default MyPlugin;
//2. 若为函数,会被做为install方法
export default function(Vue) {
}
复制代码
var MyPlugin = require('my-plugin');
Vue.use(MyPlugin, options);
复制代码
use
的内部处理和webpack相似:MyPlugin.install(Vue)
,都是使用约定的函数,将使用者注入,进行功能加强。 另外提一下,Regular
中的Component也是由一个use函数
来统一'使用'插件。Babel插件
Babel 虽然开箱即用,可是什么动做都不作。它基本上相似于 const babel = code => code;
,将代码解析以后再输出一样的代码。若是想要 Babel 作一些实际的工做,就须要为其添加插件。
//babel-plugin-demo/index
module.exports = function() {
return {
//访问者
visitor: {
BinaryExpression(path, state) {
//对AST对象进行变换操做
}
/**
*等价于
* BinaryExpression: {
* enter() {
* //进入节点,相应的存在exit()
* }
* }
*
**/
}
}
}
复制代码
上面定义了一个简单的访问者(visitor)
,当遍历AST树的过程当中遇到type
为BinaryExpression
的节点,就会调用BinaryExpression()
方法。//.babelrc
{
...
"plugin": [
[
"demo", //等价于babel-plugin-demo
{
... //state.opts
}
]
]
}
复制代码
总结
经过上面的总结,能够看到不一样框架对插件的实现大同小异:不固化依赖,而是经过对外提供"插槽"的方式,由使用者灵活的按需注入。在其中,看到了依赖注入
和装饰者模式
的影子。
平常开发中,随处可见插件的应用,好比redux的中间件等,这里不作赘述。若是可以灵活处理依赖与被依赖二者的关系,有利于提升代码的复用性,对编程思惟也是一种提高。
参考
【欢迎留言】本文是否对你有帮助,亦或有所
遗漏笔误
等,烦请告知。