中间件和插件的实现
1.vue的install实现
核心是原型上添加方法/属性
class Store {
static use (plugin, options) {
plugin(this, options)
return this
}
}
const plugin1 = function (Store) {
Store.prototype.$someFn1 = function () {}
}
const plugin2 = function (Store, options) {
Store.prototype.$someFn2 = function () {}
}
Store.use(plugin1).use(plugin2, {})
2.koa的洋葱模式中间件实现
核心是实现一个dispath方法, 这个方法会递归调用中间件, 将下个中间件作为参数next传入上个中间件
const applyApp = ctx => async (...middleware) => {
async function dispatch(index) {
let fn = middleware[index]
if (index === middleware.length) return
await fn(ctx, () => dispatch(index + 1))
}
await dispatch(0)
}
const a1 = async (ctx, next) => {
ctx.ctxProp += '1'
console.log('进入1')
await sleep(2000)
await next()
console.log('出去1')
}
const a2 = async (ctx, next) => {
ctx.ctxProp += '2'
console.log('进入2')
await next( + 'C')
console.log('出去2')
}
const a3 = async (ctx, next) => {
ctx.ctxProp += '3'
console.log('进入3')
await next()
console.log('出去3')
}
const store = { ctxProp: '' }
applyApp(store)(a1, a2, a3)
.then((v) => {
console.log(store)
})
.catch(err => console.log(err))
- 借助tapale实现hook插件
const {
SyncHook
} = require("tapable");
// 一个插件, 在store中注册created/destory生命周期回调, 原型添加一个destory方法
const somePlugin = function (pluginApi, options) {
pluginApi.onHook('created', (instance) => {
console.log('created', instance.status)
})
pluginApi.onHook('destory', () => {
console.log('destory')
})
pluginApi.addMethod('destory', () => {
pluginApi.callHook('destory')
})
}
const config = {
plugins: [somePlugin]
}
class Store {
Hooks = {
created: new SyncHook(['instance'])
}
status = ''
constructor () {
this.loadPlugin()
this.Hooks.created.call(this)
}
loadPlugin () {
config.plugins.map(plugin => plugin(new PluginApi(this)));
}
}
// 提取的plugin管理api, 提供注册/触发Hook, 原型添加方法
class PluginApi {
constructor (store) {
this.store = store
}
onHook (name, handler) {
if (!this.store.Hooks[name]) {
this.store.Hooks[name] = new SyncHook();
}
this.store.Hooks[name].tap(name, handler)
}
callHook (name) {
this.store.Hooks[name].call()
}
addMethod (name, fn) {
this.store.__proto__[name] = fn
}
}
const store = new Store()
setTimeout(() => {
store.destory()
}, 1000)