webpack成神(4)


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

原理

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

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

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

异步串行 瀑布流钩子函数

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

打印 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

基于 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

打印

hahah node 第一个传递给第二个 react 第二个传递给第三个 end