引子
webpack核心库tapable的源码探究,Tapable是webpack中重要一环,它的工作就是将各个插件串联起来,确保在对应的时机执行对应的loader或者plugin。Tapable的实现类似于promise redux等,核心原理依赖于发布订阅模式。
我们不是一比一复刻源码,而是对于源码的某一些核心方法进行自己的实现,具体如下
\
1.SyncBaikHook
先看使用示例:
const { SyncBailHook } = require('tapable')
class Lesson {
constructor() {
this.hook = {
//这里的['name'],代表call可传几个参数给tap的第二个参数
//['name','age'],表示this.hook.arch.call('小明',18)
arch: new SyncBailHook(['name'])
}
}
tap() {
this.hook.arch.tap('语文', (name) => {
console.log(name, '---语文')
})
this.hook.arch.tap('数学', (name) => {
console.log(name, '---数学')
})
}
start() {
this.hook.arch.call('小明')
}
}
let l = new Lesson()
l.tap()
l.start()
执行结果:
如果上一次执行有返回,终止执行\
class Lesson {
constructor() {
this.hook = {
arch: new SyncBailHook(['name'])
}
}
tap() {
this.hook.arch.tap('语文', (name,) => {
console.log(name, '---语文')
++ return '语文学习不达标'
})
this.hook.arch.tap('数学', (name,) => {
console.log(name, '---数学')
})
}
start() {
this.hook.arch.call('小明')
}
}
let l = new Lesson()
l.tap()
l.start()
执行结果:
实现一个SyncBailHook\
class SyncBailHook1{
constructor(name) {
this.tasks=[]
}
tap(name,tasks) {
this.tasks.push(tasks)
}
call(...args) {
let ret;
let index = 0;
do {
//拿到每一次注册函数的执行结果,有返回,则终止执行
ret=this.tasks[index++](...args)
} while (ret===undefined && index<this.tasks.length)
}
}
\
2.SyncWaterHook
使用示例:\
const { SyncWaterfallHook } = require('tapable')
class Lesson {
constructor() {
this.hook = {
arch: new SyncWaterfallHook(['name'])
}
}
tap() {
this.hook.arch.tap('语文', (name,) => {
console.log(name, '---语文')
return '语文考了满分'
})
this.hook.arch.tap('数学', (name,) => {
console.log(name, '---数学')
return '数学考了满分'
})
this.hook.arch.tap('英语', (name,) => {
console.log(name,'---英语')
})
}
start() {
this.hook.arch.call('小明')
}
}
let l = new Lesson()
l.tap()
l.start()
执行结果:
实现一个SyncWaterHook\
class SyncWaterfallHook{
constructor(name) {
this.tasks=[]
}
tap(name,tasks) {
this.tasks.push(tasks)
}
call(...args) {
const [first, ...ohthers] = this.tasks
let ret = first(...args)
//利用reduce进行处理,acc类似于累积器,收集到上一次执行结果,传递给下一次执行
ohthers.reduce((acc, cur) => {
return cur(acc)
},ret)
}
}
3.AsyncParalleHook
使用示例:\
const { AsyncParallelHook } = require('tapable')
class Lesson {
constructor() {
this.index = 0;
this.hook = {
arch: new AsyncParallelHook(['name'])
}
}
tap() {
this.hook.arch.tapAsync('语文', (name,cb) => {
setTimeout(() => {
console.log(name, '---语文')
cb()
}, 1000);
})
this.hook.arch.tapAsync('数学', (name,cb) => {
setTimeout(() => {
console.log(name, '---数学')
cb()
},2000)
})
this.hook.arch.tapAsync('英语', (name,cb) => {
setTimeout(() => {
console.log(name, '---英语')
cb()
},1000)
})
}
start() {
this.hook.arch.callAsync('小明', () => {
console.log('学完了')
})
}
}
let l = new Lesson()
l.tap()
l.start()
执行结果:
实现一个AsyncParallelHook\
class AsyncParallelHook{
constructor(name) {
this.tasks=[]
}
tapAsync(name,tasks) {
this.tasks.push(tasks)
}
callAsync(...args) {
let finalCallback = args.pop() //取出最终函数
let index = 0;
//tap的回调函数,当每一个注册到tasks数组里面函数执行完成,调用callAsync的回调(即第二个参数)
const done = () => {
index++
if (index === this.tasks.length) {
finalCallback()
}
}
this.tasks.forEach((task) => {
task(...args,done)
})
}
}
\
4.AsyncSeriesHook
使用示例1:\
const { AsyncSeriesHook } = require('tapable')
class Lesson {
constructor() {
this.index = 0;
this.hook = {
arch: new AsyncSeriesHook(['name'])
}
}
tap() {
this.hook.arch.tapAsync('语文', (name,cb) => {
setTimeout(() => {
console.log(name, '---语文')
cb()
}, 1000);
})
this.hook.arch.tapAsync('数学', (name,cb) => {
setTimeout(() => {
console.log(name, '---数学')
cb()
},2000)
})
this.hook.arch.tapAsync('英语', (name,cb) => {
setTimeout(() => {
console.log(name, '---英语')
cb()
},1000)
})
}
start() {
this.hook.arch.callAsync('小明', () => {
console.log('学完了')
})
}
}
let l = new Lesson()
l.tap()
l.start()
执行结果:\
实现一个AsyncSeriesHook:
class AsyncSeriesHook{
constructor(name) {
this.tasks=[]
}
tapAsync(name,tasks) {
this.tasks.push(tasks)
}
callAsync(...args) {
let finalCallback=args.pop()
let index = 0;
//并发串行,这里实现的回调,保证了在前一个task执行完成后,在调起下一个
let next = () => {
if(this.tasks.length===index) return finalCallback()
let task=this.tasks[index++]
task(...args,next)
}
next()
}
}
\
使用示例2:\
const { AsyncSeriesHook } = require('tapable')
class Lesson {
constructor() {
this.index = 0;
this.hook = {
arch: new AsyncSeriesHook(['name'])
}
}
tap() {
this.hook.arch.tapPromise('语文', (name) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, '---语文')
resolve()
}, 1000);
})
})
this.hook.arch.tapPromise('数学', (name) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, '---数学')
resolve()
},2000)
})
})
this.hook.arch.tapPromise('英语', (name) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, '---英语')
resolve()
},1000)
})
})
}
start() {
this.hook.arch.promise('小明').then(() => {
console.log('学完啦')
})
}
}
let l = new Lesson()
l.tap()
l.start()
\
实现一个AsyncSeriesHook\
class AsyncSeriesHook{
constructor(name) {
this.tasks=[]
}
tapPromise(name,tasks) {
this.tasks.push(tasks)
}
promise(...args) {
const [first, ...others] = this.tasks
//这里还是利用reduce,把后一个task放到前一个task的.then函数里面,保证了执行顺序
return others.reduce((acc, cur) => {
return acc.then(() => cur(...args))
},first(...args))
}
}
5.AsyncSeriesWaterfallHook
使用示例:\
const { AsyncSeriesWaterfallHook } = require('tapable')
class Lesson {
constructor() {
this.index = 0;
this.hook = {
arch: new AsyncSeriesWaterfallHook(['name'])
}
}
tap() {
this.hook.arch.tapAsync('语文', (name,cb) => {
setTimeout(() => {
console.log(name, '---语文')
//回调第一个参数标识是否出错,终止执行,有值标识出错
cb(null,100)
}, 1000);
})
this.hook.arch.tapAsync('数学', (name,cb) => {
setTimeout(() => {
console.log(name, '---数学')
cb(null,99)
},2000)
})
this.hook.arch.tapAsync('英语', (name,cb) => {
setTimeout(() => {
console.log(name, '---英语')
cb(null,59)
},1000)
})
}
start() {
this.hook.arch.callAsync('小明', () => {
console.log('学完了')
})
}
}
let l = new Lesson()
l.tap()
l.start()
执行结果:
实现一个AsyncSeriesWaterfallHook:\
class AsyncSeriesWaterfallHook{
constructor(name) {
this.tasks=[]
}
tapAsync(name,tasks) {
this.tasks.push(tasks)
}
callAsync(...args) {
let finalCallback=args.pop()
let index = 0;
let next = (err,data) => {
let task = this.tasks[index]
//如果出错,直接执行callAsync的回调函数
if (err) {
return finalCallback()
}
//第一次执行,传递callAsync的参数
if (index === 0) {
task(...args,next)
} else {
//后面每次执行,传递tapAsync传递的回调参数
task(data,next)
}
index++
}
next(null,...args)
}
}