tapable 库使用和原理分析
webpack 源码中使用这个库来事件流程。
tapable 库中有三种注册方法,tab 同步注册,tabAsync 异步注册(回调方式),tapPromise 异步注册(promise 方式)
调用也是三种方式,call 同步调用,callAsync 异步调用,promise 异步调用
同步注册
let {SyncHook} = require('tapable'); class Lesson { constructor () { this.hooks = { arch: new SyncHook(['name']) } } tap () { this.hooks.arch.tap('node', function (name) { console.log(`${name}学习node课程`) }); this.hooks.arch.tap('react', function (name) { console.log(`${name}学习react课程`) }); } start () { this.hooks.arch.call('张三') } } let lesson = new Lesson(); lesson.tap(); lesson.start(); console.log('学习完成')
原理
class SyncHook { constructor (args) { this.tasks = []; } call (...args) { this.tasks.forEach(task => task(...args)) } tap (name, task) { this.tasks.push(task) } }
异步回调注册
let {AsyncParallelHook} = require('tapable') class Lesson { constructor () { this.hooks = { arch: new AsyncParallelHook(['name']) } } tap () { this.hooks.arch.tapAsync('node', function (name, cb) { setTimeout(() => { console.log(`${name}学习node课程`) cb(); }, 1000); }); this.hooks.arch.tapAsync('react', function (name, cb) { setTimeout(() => { console.log(`${name}学习react课程`) cb(); }, 1000); }); } start () { this.hooks.arch.callAsync('张三', () => { console.log('学习完成') }) } }
原理
class AsyncParallelHook { constructor (args) { this.tasks = []; } callAsync (...args) { let finalCallback = args.pop(); let index = 0; const done = () => { index++; if (index === this.tasks.length) { finalCallback() } } this.tasks.forEach(task => { task(...args, done) }) } tapAsync (name, task) { this.tasks.push(task) } }
异步 promise 注册
let {AsyncParallelHook} = require('tapable') class Lesson { constructor () { this.hooks = { arch: new AsyncParallelHook(['name']) } } tap () { this.hooks.arch.tapPromise('node', function (name) { return new Promise((resolve, reject) => { setTimeout(() => { console.log(`${name}学习node课程`) resolve() }, 1000); }) }); this.hooks.arch.tapPromise('react', function (name) { return new Promise((resolve, reject) => { setTimeout(() => { console.log(`${name}学习react课程`) resolve() }, 1000); }) }); } start () { this.hooks.arch.promise('张三').then(() => { console.log('学习完成') }) } }
原理
class AsyncParallelHook { constructor (args) { this.tasks = []; } promise(...args) { let tasks = this.tasks.map(task => task(...args)); return Promise.all(tasks) } tapPromise (name, task) { this.tasks.push(task) } }
上面包含两种异步处理方式都是异步并发的,并不能保证执行的先后顺序,那么接下来介绍异步并行执行的钩子函数。
异步串行回调形式
class Lesson { constructor() { this.hooks = { arch: new AsyncSeriesHook(['name']) } } tap() { this.hooks.arch.tapAsync('node', function (name, cb) { setTimeout(() => { console.log(`${name}学习node课程`) cb() }, 1000); }); this.hooks.arch.tapAsync('react', function (name, cb) { setTimeout(() => { console.log(`${name}学习react课程`) cb() }, 1000); }) } start() { this.hooks.arch.callAsync('张三', () => { console.log('学习完成') }) } }
原理(express 的 next 方法实现类似)
class AsyncSeriesHook { constructor (args) { this.tasks = []; } callAsync(...args) { let finalCallback = args.pop() let index = 0; let next = () => { if (this.tasks.length === index) return finalCallback(); let task = this.tasks[index++]; task(...args, next) } next() } tapAsync (name, task) { this.tasks.push(task) } }
异步串行 promise 形式
class Lesson { constructor () { this.hooks = { arch: new AsyncSeriesHook(['name']) } } tap () { this.hooks.arch.tapPromise('node', function (name) { return new Promise((resolve, reject) => { setTimeout(() => { console.log(`${name}学习node课程`) resolve() }, 1000); }) }); this.hooks.arch.tapPromise('react', function (name) { return new Promise((resolve, reject) => { setTimeout(() => { console.log(`${name}学习react课程`) resolve() }, 1000); }) }); } start () { this.hooks.arch.promise('张三').then(() => { console.log('学习完成') }) } }
原理(redux 中间件原理类似)
class AsyncSeriesHook { constructor(args) { this.tasks = []; } promise(...args) { let [first, ...others] = this.tasks; return others.reduce((p, n) => { return p.then(() => n(...args)) }, first(...args)) } tapPromise(name, task) { this.tasks.push(task) } }
异步瀑布流串行形式
意思是一个出错了会终端后续执行
let { AsyncSeriesWaterfallHook } = require('tapable') class Lesson { constructor() { this.hooks = { arch: new AsyncSeriesWaterfallHook(['name']) } } tap() { this.hooks.arch.tapAsync('node', function (name, cb) { setTimeout(() => { console.log(`${name}学习node课程`) cb(null, 'data') }, 1000); }); this.hooks.arch.tapAsync('react', function (data, cb) { setTimeout(() => { console.log(data) cb() }, 1000); }) } start() { this.hooks.arch.callAsync('张三', () => { console.log('学习完成') }) } }
原理
class AsyncSeriesWaterfallHook { constructor (args) { this.tasks = []; } callAsync(...args) { // 类似于express的next let finalCallback = args.pop() let index = 0; let next = (err, data) => { let task = this.tasks[index++]; if (!task) return finalCallback(); if (index === 0) { task(...args, next) } else { task(data, next) } index++ } next() } tapAsync (name, task) { this.tasks.push(task) } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论