【本文主旨】僅探討插件系統的實現,不針對某個具體功能的插件。
【關鍵詞】依賴與被依賴者的管理、賦能、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的中間件等,這裏不作贅述。若是可以靈活處理依賴與被依賴二者的關係,有利於提升代碼的複用性,對編程思惟也是一種提高。
參考
【歡迎留言】本文是否對你有幫助,亦或有所
遺漏筆誤
等,煩請告知。