webpack成神(3)
Yu Lei 2019-3-27
webpack
webpack 成神
webpack 的底层原理
webpack 本质上是一种事件流机制,他就是将各个插件串联起来。实现这一机制的就是 tapable tapable 类似于 nodejs 中的 events 库,核心原理也是发布订阅模式
webpack 编译 模块 compiler 引用了 tapable
同步钩子,也就是同步订阅发布模式
const {
SyncHook //同步钩子模块
} = require('tapable')
class Lession {
constructor() {
this.hook = {
// 为 arch 确定 钩子类型 和钩子内传递的函数
arch: new SyncHook(['name'])
}
}
tap() {
// 注册监听函数
this.hook.arch.tap('node', function(name) {
console.log('node', name)
})
this.hook.arch.tap('react', function(name) {
console.log('react', name)
})
}
srart() {
// 触发 监听函数 依次执行 call 内部的参数 指向 function 的参数 name
this.hook.arch.call('jw')
}
}
let l = new Lession()
l.tap()
l.srart()
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
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
tapable 的原理
```javascript
class SyncHook { //自己实现一个同步的钩子
constructor (args) {
this.takes = [] //定义一个集合
}
tap (name, task) {
// 同步注册
this.takes.push(task)//注册事件推进集合
}
call (...args) {
// 执行
this.takes.forEach(task => task(...args))//执行过程按照顺序执行
}
}
class Lession {
constructor () {
this.hook = {
arch: new SyncHook(['name'])
}
}
tap () {
this.hook.arch.tap('react', function (name) {
console.log('react', name)
})
this.hook.arch.tap('vue', function (name) {
console.log('vue', name)
})
}
start () {
this.hook.arch.call('jw')
}
}
let ession = new Lession()
ession.tap()
ession.start()
```
同步钩子,有熔断机制的同步钩子函数
const { SyncBailHook } = require('tapable')
class Lession {
constructor() {
this.hook = {
arch: new SyncBailHook(['name'])
}
}
tap() {
this.hook.arch.tap('react', function(name) {
console.log('react', name)
return '想停止学习' // 在这里返回了一个非 undefind 的值 所以就不会继续向下执行了 //类似于 数组中的 some 和 promise 中的 some
})
this.hook.arch.tap('vue', function(name) {
console.log('vue', name)
})
}
start() {
this.hook.arch.call('jw')
}
}
const l = new Lession()
l.tap()
l.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
底层实现原理
class SyncBileHook {
constructor(args) {
this.takes = []
}
tap(name, task) {
// 同步注册
this.takes.push(task)
}
call(...args) {
// 执行
let ret = null
let index = 0 //主要是这里
do {
ret = this.takes[index++](...args)
//执行下一个函数的条件是上一个函数的返回值不是 undefind 或者 所有的函数未执行完成
} while (!ret && index < this.takes.length)
}
}
class Lession {
constructor() {
this.hook = {
arch: new SyncBileHook(['name'])
}
}
tap() {
this.hook.arch.tap('react', function(name) {
console.log('react', name)
return '停止向下执行' //这里有返回值 所以向下执行的动力失去了
})
this.hook.arch.tap('vue', function(name) {
console.log('vue', name)
})
}
start() {
this.hook.arch.call('jw')
}
}
let ession = new Lession()
ession.tap()
ession.start()
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
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
syncwaterfallhook 瀑布流钩子函数
也就是上一个 钩子的函数的 结果会成为下一个钩子函数的接受的数据,常用于串行方法,当一个函数返回 undefined 的时候会有问题
const { SyncWaterfallHook } = require('tapable')
class Lession {
constructor() {
this.hook = {
arch: new SyncWaterfallHook(['name'])
}
}
tap() {
this.hook.arch.tap('react', function(name) {
console.log('react', name)
return 'react 学的还不错' // 这个返回值 对应下一个 注册函数的 参数 data
//如果注册多个 函数 就会 每一个函数的 返回值成为下一个函数的 参数
})
this.hook.arch.tap('vue', function(data) {
console.log('vue', data)
})
}
start() {
this.hook.arch.call('jw')
}
}
const l = new Lession()
l.tap()
l.start()
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
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
实现原理
class SyncWaterFallHook {
constructor(args) {
this.takes = []
}
tap(name, task) {
// 同步注册
this.takes.push(task)
}
call(...args) {
let [first, ...others] = this.takes
let ret = first(...args) // 第一个参数 执行给定参数
others.reduce((a, b) => {
return b(a) // 每次执行的结果传递给下一个函数作为参数
}, ret) // 把结果传递到reduce 中
}
}
class Lession {
constructor() {
this.hook = {
arch: new SyncWaterFallHook(['name'])
}
}
tap() {
this.hook.arch.tap('react', function(name) {
console.log('react', name)
return '停止向下执行'
})
this.hook.arch.tap('vue', function(data) {
console.log('vue', data)
return 'vue ok'
})
this.hook.arch.tap('webpack', function(data) {
console.log('webpack', data)
})
}
start() {
this.hook.arch.call('jw')
}
}
let ession = new Lession()
ession.tap()
ession.start()
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
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
syncloophook
当遇到某个不返回 undefinded 钩子函数的时候会循环执行
const { SyncLoopHook } = require('tapable')
class Lession {
constructor() {
this.index = 0
this.hook = {
arch: new SyncLoopHook(['name'])
}
}
tap() {
this.hook.arch.tap('react', name => {
//这里箭头函数才能把this 指向到Lession
console.log('react', name)
return ++this.index === 5 ? undefined : '继续学习'
// 在这里返回了一个非undefind 的值 所以就不会继续向下执行了 //类似于 数组中的some 和promise 中的some
})
this.hook.arch.tap('vue', function(data) {
console.log('vue', data)
})
}
start() {
this.hook.arch.call('jw')
}
}
const l = new Lession()
l.tap()
l.start()
//打印
//react jw
// react jw
//react jw
//react jw
//react jw
// vue jw
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
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
实现原理
class SyncWaterFallHook {
constructor (args) {
this.takes = []
}
tap (name, task) {
// 同步注册
this.takes.push(task)
}
call (...args) {
this.takes.forEach(task => {
let re
do {
re = task(...args)
}
while (re !== undefined)
})
}
}
class Lession {
constructor () {
this.index = 0
this.hook = {
arch: new SyncWaterFallHook(['name'])
}
}
tap () {
this.hook.arch.tap('react', (name) => {
console.log('react', name)
return ++this.index === 5 ? undefined : '继续'
})
this.hook.arch.tap('vue', function (data) {
console.log('vue', data)
})
this.hook.arch.tap('webpack', function (data) {
console.log('webpack', data)
})
}
start () {
this.hook.arch.call('jw')
}
}
let ession = new Lession()
ession.tap()
ession.start()
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
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