webpack成神(4)
Yu Lei 2019-4-11
webpack
webpack 成神
webpack 的底层原理
webpack 本质上是一种事件流机制,他就是将各个插件串联起来。实现这一机制的就是 tapable tapable 类似于 nodejs 中的 events 库,核心原理也是发布订阅模式
webpack 编译 模块 compiler 引用了 tapable
tapable 的注册和执行有同步和异步注册
tap 同步注册
tapAsync 异步注册 参数(‘name’,function(参数,判断回调){ })
tappromise 注册成promise 模式 返回需要一个promise 使用 promise()方法 来触发
taps 传入一个数组 同时注册多个
call 同步执行
callAsync 异步执行 (‘参数’,回调函数)
异步事件钩子,也就是异步的订阅发布模式 AsyncParallelHook 并行
tapAsync 注册 callAsync 执行
```javascript
const {
AsyncParallelHook
} = require('tapable')
class Lession {
constructor () {
this.hooks = {
arch: new AsyncParallelHook(['name'])
}
}
tap () {
this.hooks.arch.tapAsync('node', (name, cb) => {
setTimeout(() => {
console.log(name, 'node')
cb()
}, 2000)
})
this.hooks.arch.tapAsync('react', (name, cb) => {
setTimeout(() => {
console.log(name, 'react')
cb()
}, 2000)
})
}
call () {
this.hooks.arch.callAsync('hahaha', () => {
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
//等待2秒后 输出
//hahaha node
//hahaha react
//end
```
实现原理 使用计数器的原理
```javascript
class AsyncParallelHook {
constructor (args) {
this.takes = []
}
tapAsync (name, task) {
// 同步注册
this.takes.push(task)
}
callAsync (...args) {
//拿出最终的函数
const finalCallback = args.pop()
let index = 0
let done = (params) => {//计数器 每次执行函数都会增加数量
index++
if (index === this.takes.length) {//当 次数与任务链相同的时候 执行最终函数
finalCallback()
}
}
this.takes.forEach(task => {//保证并行
task(...args, done)
})
}
}
class Lession {
constructor () {
this.hooks = {
arch: new AsyncParallelHook(['name'])
}
}
tap () {
this.hooks.arch.tapAsync('node', (name, cb) => {
setTimeout(() => {
console.log(name, 'node')
cb()
}, 2000)
})
this.hooks.arch.tapAsync('react', (name, cb) => {
setTimeout(() => {
console.log(name, 'react')
cb()
}, 2000)
})
}
call () {
this.hooks.arch.callAsync('hahaha', () => {
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
```
tappromise 模式下的并行钩子 tapPromise 注册 promise 执行
```javascript
const {
AsyncParallelHook
} = require('tapable')
class Lession {
constructor () {
this.hooks = {
arch: new AsyncParallelHook(['name'])
}
}
tap () {
this.hooks.arch.tapPromise('node', (name) => { //tapPromise 注册
return new Promise((resolve, reject) => { //返回一个promise
setTimeout(() => {
console.log(name, 'node')
resolve() //成功调用这个函数 并且可以把成功的结果返回出去
}, 1000)
})
})
this.hooks.arch.tapPromise('react', (name) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'react')
resolve()
}, 1000)
})
})
}
call () {
this.hooks.arch.promise('hahaha').then(() => {//使用promise()方法触发注册的钩子函数 并且 有.then 执行最终的回调函数
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
//内部的实现机理其实是一个promise.all()
```
promise 注册的实现原理
```javascript
class AsyncParallelHook {
constructor(args) {
this.takes = []
}
tapPromise(name, task) {
// 同步注册
this.takes.push(task) //把任务推进队列
}
promise(...args) {
//把任务队列中的任务全部取出 并且传入参数 构建一个新的 promise 队列 ,因为在此时 primise 没有then 触发 所有promise 都处于pending 状态
let newtakes = this.takes.map(task => task(...args))
//[ Promise { <pending> }, Promise { <pending> } ] '===='
console.log(newtakes, '====')
return Promise.all(newtakes) //执行异步队列
}
}
class Lession {
constructor() {
this.hooks = {
arch: new AsyncParallelHook(['name'])
}
}
tap() {
this.hooks.arch.tapPromise('node', name => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'node')
resolve()
}, 1000)
})
})
this.hooks.arch.tapPromise('react', name => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'react')
resolve()
}, 1000)
})
})
}
call() {
this.hooks.arch.promise('hahaha').then(() => {//获取异步队列执行结果之后执行回调函数
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
```
异步事件带保险的钩子, AsyncParallelBailHook 并行
promise 版本的使用
```javascript
const { AsyncParallelBailHook } = require('tapable')
class Lession {
constructor() {
this.hooks = {
arch: new AsyncParallelBailHook(['name'])
}
}
tap() {
this.hooks.arch.tapPromise('node', name => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'node')
resolve('node')
}, 1000)
})
})
this.hooks.arch.tapPromise('react', name => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'react')
reject()
}, 1000)
})
})
}
call() {
this.hooks.arch
.promise('hahaha')
.then(data => {
console.log(data, 'data====')
console.log('end')
})
.catch(e => {
console.log(e)
})
}
}
let l = new Lession()
l.tap()
l.call()
```
异步串行, AsyncSeriesHook 钩子
一个执行完成再执行下一个
使用
const { AsyncSeriesHook } = require('tapable')
class Lession {
constructor() {
this.hooks = {
arch: new AsyncSeriesHook(['name'])
}
}
tap() {
this.hooks.arch.tapAsync('node', (name, cb) => {
setTimeout(() => {
console.log(name, 'node')
cb()
}, 1000)
})
this.hooks.arch.tapAsync('react', (name, cb) => {
setTimeout(() => {
console.log(name, 'react')
cb()
}, 1000)
})
}
call() {
this.hooks.arch.callAsync('hahaha', () => {
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
//等一秒 然后执行第一个
//等一秒 执行第二个
//执行最后的回调
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
原理
class AsyncSeriesHook {
constructor(args) {
this.takes = []
}
tapAsync(name, task) {
// 同步注册
this.takes.push(task)
}
callAsync(...args) {
let finalCallback = args.pop()
let index = 0
let next = () => {
//这个是一个异步的迭代函数 每次需要执行的异步函数 放在迭代函数内部执行
if (this.takes.length === index) return finalCallback() //最终需要执行 回调函数
let task = this.task[index++]
task(...args, next) //执行的异步函数
}
next() //初始启动
}
}
class Lession {
constructor() {
this.hooks = {
arch: new AsyncSeriesHook(['name'])
}
}
tap() {
this.hooks.arch.tapAsync('node', (name, cb) => {
setTimeout(() => {
console.log(name, 'node')
cb()
}, 1000)
})
this.hooks.arch.tapAsync('react', (name, cb) => {
setTimeout(() => {
console.log(name, 'node')
cb()
}, 1000)
})
}
call() {
this.hooks.arch.callAsync('hahah', () => {
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
promise 版本 使用
const { AsyncSeriesHook } = require('tapable')
class Lession {
constructor() {
this.hooks = {
arch: new AsyncSeriesHook(['name'])
}
}
tap() {
this.hooks.arch.tapPromise('node', name => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'node')
resolve()
}, 3000)
})
})
this.hooks.arch.tapPromise('react', name => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'react')
resolve()
}, 1000)
})
})
}
call() {
this.hooks.arch.promise('hahaha').then(() => {
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
promise 版本实现
class AsyncSeriesHook {
constructor(args) {
this.takes = []
}
tapPromise(name, task) {
// 同步注册
this.takes.push(task)
}
promise(...args) {
let [first, ...others] = this.takes //解构 promise 数组
//最终返回 一个promise 利用最终的。then 执行
return others.reduce((p, n) => {
//利用数组的收敛
return p.then(() => n(...args)) //每次执行当前的promise 并且把下一个要执行的装置参数 并且反回出去
}, first(...args)) //传入第一个要执行的函数
}
}
class Lession {
constructor() {
this.hooks = {
arch: new AsyncSeriesHook(['name'])
}
}
tap() {
this.hooks.arch.tapPromise('node', name => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'node')
resolve()
}, 1000)
})
})
this.hooks.arch.tapPromise('react', (name, cb) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'react')
resolve()
}, 1000)
})
})
}
call() {
this.hooks.arch.promise('hahah').then(() => {
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
异步串行 瀑布流钩子函数
async 实现
const { AsyncSeriesWaterfallHook } = require('tapable')
class Lession {
constructor() {
this.hooks = {
arch: new AsyncSeriesWaterfallHook(['name'])
}
}
tap() {
this.hooks.arch.tapAsync('node', (name, cb) => {
setTimeout(() => {
console.log(name, 'node')
cb(null, '第一个回调传递给第二个的结果')
}, 3000)
})
this.hooks.arch.tapAsync('react', (name, cb) => {
setTimeout(() => {
console.log(name, 'react')
cb()
}, 1000)
})
}
call() {
this.hooks.arch.callAsync('hahaha', () => {
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
打印 hahaha node 第一个回调传递给第二个的结果 react end
如果 中间有一个 cb('error')
那么 会跳过钩子函数直接执行最后的回调函数
async 实现
callAsync (...args) {
let finalFun = args.pop()
let index = 0
let next = (err, data) => {
if (err === 'error') finalFun()
let take = this.takes[index]
if (!take) return finalFun()
if (index === 0) {
take(...args, next)
} else {
take(data, next)
}
index++
}
next()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
基于 promise 的实现
class AsyncSeriesWaterfallHook {
constructor(args) {
this.takes = []
}
tapPromise(name, task) {
// 同步注册
this.takes.push(task)
}
promise(...args) {
let [first, ...others] = this.takes
return others.reduce((p, n) => {
return p.then(r => n(r))
}, first(...args))
}
}
class Lession {
constructor() {
this.hooks = {
arch: new AsyncSeriesWaterfallHook(['name'])
}
}
tap() {
this.hooks.arch.tapPromise('node', name => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'node')
resolve('第一个传递给第二个')
}, 1000)
})
})
this.hooks.arch.tapPromise('react', (name, cb) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name, 'react')
resolve('第二个传递给第三个')
}, 1000)
})
})
}
call() {
this.hooks.arch.promise('hahah').then(r => {
console.log(r)
console.log('end')
})
}
}
let l = new Lession()
l.tap()
l.call()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
打印
hahah node 第一个传递给第二个 react 第二个传递给第三个 end