经常在使用各种js类库/框架中,往往他们都提供了各种插件机制,来新增补充各种功能,下面想分享一下最近学习各种框架,自己总结一下插件的实现和使用的方式
哪些情况下需要使用插件或者钩子
前端开发中,我们需要设计一个模块,或者一个小型的框架和库,但是前期的功能,比较少,实现是一个简单的版本,随着版本的迭代,我们需要实现的功能会越来越多,但是往往现在想推到代码重新编写,这样费时费力,如果前些实现功能时,预留插件机制或者钩子,那么在版本迭代的时候可以使用插件或者钩子来实现新功能,这样代码的复用性和维护性都往往提高不少
下面以开发一个图表组件来阐述我的思想
主逻辑
function plugins(Obj)
{
this.plugins = {}
this.plugins.Obj = Obj
}
plugins.extends = function(target){
Object.keys(target).forEach(d => {
pligins.prototype[d] = target[d]
})
}
plugins.extends({
addPlugin: function(hook, fn) {
var me = this
if (Object.keys(me.plugins).includes(hook)) {
me.plugins[hook].push(fn)
} else {
me.plugins[hook] = fn
}
},
removePlugin: function(hook, fn) {
var me = this
if (me.plugins[hook]) {
var index = me.plugins[hook].indexOf(fn)
me.plugins.splice(index, 1)
}
},
notify (hook) {
this.plugins[hook].forEach(d => {
d.call(null, this.plugins.Obj)
})
}
})
// 图表的实现
function Chart (ctx, config)
{
this.plugins = new plugins(this) // 插件的储存目录
this.config(config)
this.update(); // 数据更新和图表绘制
}
Chart.prototype.initialize = function () {
var me = this
// 埋下插件的钩子
me.plugins.notify('beforeInit')
// 执行其他逻辑
me.plugins.notify('afterInit')
// 后置逻辑
}
调用代码
var chart = new Chart(canvas, config)
chart.plugins.addPlugin ('beforeInit', function(chart) {
console.log('前置插件')
})
chart.plugins.addPlugin ('afterInit', function(chart) {
console.log('后置插件')
})
chart.initialize() // 初始化
以上简单实现来一个简单的函数式插件,需要的逻辑都封装在一个插件函数中,当然这样的实现,可以在前置、后置补充很多逻辑,这种封装做的还不是够完美,其实可以把插件实现类似vue.use() 这样来安装,有一个契约规则,让插件编写的规范化
插件和钩子的思想
对于一个前端组件,其实我们在编写的时候也应该有各种生命周期的概念,这样我们可以更加合理的来组织我们的代码。
以图表组件为例说明。
对于这个组件,应该至少有三个阶段, 初始化, 数据变化, 组件析构
所以,我们需要做插件和钩子应该有两种思想。
做钩子函数
可以利用刚刚写的plugins插件实现在生命周期里的钩子函数,在三个阶段 或者 任何更新函数前,都可以埋点。做插件
插件其实可以做的更加复杂
我们可以提供类似这样的代码
var plugins = {
data,
afterinit() {},
update() {},
beforeinit() {}
}
来注册在生命周期中任何阶段都可以执行的逻辑。
- 扩展类库的本上的方法
jQuery 通过 extend 来提供了 修改原型链上的方法和扩展明名空间上的静态方法
- 拓展组件ui 库
现在流行的mvvm类库,其实都提供了 组件注册的方式来使用。