webpack成神(3)


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

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

底层实现原理

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

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

实现原理

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

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

实现原理

 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