前言
- 上一篇是打包规律,这篇是构建流程,了解构建流程便于写插件。
Tapable
- Tapable是webpack用的类似发布订阅的钩子。
- 其中sync开头的钩子是同步钩子,async开头的钩子是异步钩子。
- async钩子里还分成串行Series钩子和并行Parallel钩子。
- 带有Bail字段的是保险式,只要监听函数有返回值且不为undefined,跳过之后的监听函数。
- 带有Waterfall字段的是上一步的返回值交给下一步使用。
- 带有Loop字段是循环类型,如果该监听函数返回true,则这个监听函数会反复执行,如果返回undefined则退出循环。
同步钩子
SyncHook
const { SyncHook } = require('tapable')
let hook = new SyncHook(['name', 'age'])
hook.tap('1', (name, age) => {
console.log(1, name, age)
})
hook.tap('2', (name, age) => {
console.log(2, name, age)
})
hook.call('yehuozhili', 111)
- 就是个发布订阅,在call的时候把tap进其内部的数组取出来执行。
- 就相当于这个:
class SyncHook {
constructor(args) {
this.args = args
this.arr = []
}
tap(name, func) {
this.arr.push(func)
}
call(...args) {
this.arr.forEach(fn => fn(...args));
}
}
SyncBailHook
let hook = new SyncBailHook(['name', 'age'])
hook.tap('1', (name, age) => {
console.log(1, name, age)
})
hook.tap('2', (name, age) => {
console.log(2, name, age)
return 2
})
hook.tap('3', (name, age) => {
console.log(3, name, age)
})
hook.call('yehuozhili', 111)
- 这玩意是只要有个有返回值就不往下执行了。
- 实现就是把那个forEach改成for就可以中断了。获取函数的返回值,有值就break就完了。
SyncWaterfallHook
let hook = new SyncWaterfallHook(['name', 'age'])
hook.tap('1', (name, age) => {
console.log(1, name, age)
return 1
})
hook.tap('2', (data, age) => {
console.log(2, data, age)
})
hook.tap('3', (data, age) => {
console.log(3, data, age)
})
hook.call('yehuozhili', 111)
- 这玩意特点就是返回值会传给下一个的第一个参数。
- 实现就是for循环里拿到第一个参数,和返回值对比,返回值有东西就用返回值,否则用参数。
SyncLoopHook
let counter1 = 0
let counter2 = 0
let counter3 = 0
let hook = new SyncLoopHook(['name', 'age'])
hook.tap('1', (name, age) => {
console.log(1, name, age)
if (++counter1 == 1) {
counter1 = 0
return
}
return true
})
hook.tap('2', (data, age) => {
console.log(2, data, age)
if (++counter2 == 2) {
counter2 = 0
return
}
return true
})
hook.tap('3', (data, age) => {
console.log(3, data, age)
if (++counter3 == 3) {
counter3 = 0
return
}
return true
})
hook.call('yehuozhili', 111)
- 先走1,然后因为返回undefined,所以走2,走2返回true,所以得从头走一遍,就又是1和2,这时返回了undefined,就走3,3是true,所以又从头走1走2,这个2还算true,回去继续走1和2,再3,3仍是true,再走次12123,最后返回undefined退出。一共15行输出。
- 实现:
class SyncLoopHook {
constructor(args) {
this.args = args
this.arr = []
}
tap(name, func) {
this.arr.push(func)
}
call(...args) {
let loop = true
while (loop) {
for (let i = 0; i < this.arr.length; i++) {
let fn = this.arr[i]
let res = fn(...args)
loop = typeof res != 'undefined'
if (loop) break
}
}
}
}
异步钩子
AsyncParallelHook
let hook = new AsyncParallelHook(['name', 'age'])
hook.tap('1', (name, age) => {
console.log(1, name, age)
})
hook.tap('2', (data, age) => {
console.log(2, data, age)
})
hook.tap('3', (data, age) => {
console.log(3, data, age)
})
hook.callAsync('yehuozhili', 111, err => {
console.log('执行完成')
})
- 异步钩子没有call,只有callAsync,通过回调来拿执行完成结果。
- 实现就是把最后个参数取出来等循环执行完调用它就行了。
- 这个还可以注册异步函数:
let hook = new AsyncParallelHook(['name', 'age'])
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1)
callback()
}, 1000);
})
hook.tapAsync('2', (data, age, callback) => {
setTimeout(() => {
console.log(2)
callback()
}, 1000);
})
hook.tapAsync('3', (data, age, callback) => {
setTimeout(() => {
console.log(3)
callback()
}, 1000);
})
hook.callAsync('yehuozhili', 111, err => {
console.log('执行完成')
})
- 这个就有点像Promise。它有个特点,就是同时并行开始执行。
- 实现:
class AsyncParallelHook {
constructor(args) {
this.args = args
this.arr = []
}
tapAsync(name, func) {
this.arr.push(func)
}
callAsync(...args) {
let argsarr = args
let fcallback= argsarr.pop()
let i = 0
const done=()=>{
if(++i===this.arr.length){
fcallback
}
}
this.arr.forEach(fn=>fn(...argsarr,done))
}
}
let hook = new AsyncParallelHook(['name', 'age'])
hook.tapPromise('1', (name, age) => {
return new Promise((res, rej) => {
setTimeout(() => {
console.log(1)
res()
}, 1000);
})
})
hook.tapPromise('2', (data, age, callback) => {
return new Promise((res, rej) => {
setTimeout(() => {
console.log(1)
res()
}, 1000);
})
})
hook.tapPromise('3', (data, age, callback) => {
return new Promise((res, rej) => {
setTimeout(() => {
console.log(1)
res()
}, 1000);
})
})
hook.promise('yehuozhili', 111).then((res => {
console.log('ok')
}), err => {
console.log('err')
})
AsyncParallelBailHook
let hook = new AsyncParallelBailHook(['name', 'age'])
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1)
callback()
}, 1000);
})
hook.tapAsync('2', (data, age, callback) => {
setTimeout(() => {
console.log(2)
callback(111)
}, 1000);
})
hook.tapAsync('3', (data, age, callback) => {
setTimeout(() => {
console.log(3)
callback()
}, 2000);
})
hook.callAsync('yehuozhili', 111, err => {
console.log('执行完成')
})
- callback有值就提前调最后的执行完成。
- 这个钩子用Promise的话不看resolve和reject,只要给resolve或者reject里传值,就提前结束。
AsyncSeriesHook
let hook = new AsyncSeriesHook(['name', 'age'])
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1)
callback()
}, 1000);
})
hook.tapAsync('2', (data, age, callback) => {
setTimeout(() => {
console.log(2)
callback()
}, 1000);
})
hook.tapAsync('3', (data, age, callback) => {
setTimeout(() => {
console.log(3)
callback()
}, 2000);
})
hook.callAsync('yehuozhili', 111, err => {
console.log('执行完成')
})
AsyncSeriesBailHook
let hook = new AsyncSeriesBailHook(['name', 'age'])
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1)
callback()
}, 1000);
})
hook.tapAsync('2', (data, age, callback) => {
setTimeout(() => {
console.log(2)
callback(6)
}, 1000);
})
hook.tapAsync('3', (data, age, callback) => {
setTimeout(() => {
console.log(3)
callback()
}, 2000);
})
hook.callAsync('yehuozhili', 111, err => {
console.log('执行完成')
})
AsyncSeriesWaterfallHook
let hook = new AsyncSeriesWaterfallHook(['name', 'age'])
hook.tapAsync('1', (name, age, callback) => {
setTimeout(() => {
console.log(1, name, age)
callback()
}, 1000);
})
hook.tapAsync('2', (data, age, callback) => {
setTimeout(() => {
console.log(2, data, age)
callback()
}, 1000);
})
hook.tapAsync('3', (data, age, callback) => {
setTimeout(() => {
console.log(3, data, age)
callback()
}, 2000);
})
hook.callAsync('yehuozhili', 111, err => {
console.log('执行完成')
})