webpack源码分析2

Posted zhuanglog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack源码分析2相关的知识,希望对你有一定的参考价值。

15、webpack与tapable

事件驱动型事件流工作机制

负责创建bundles的compilation

tapable本身就是一个独立的库

实例化hook注册时间监听

通过hook出发事件监听

执行懒编译

hook本质就是tapable实例对象

hook执行机制分为同步和异步

Hook执行特点:

普通钩子:监听器之间互相不干扰

bailhook:熔断钩子,某个监听返回undefined时后续不执行

waterfallhook:瀑布钩子,上一个监听返回值可以传给下一个

loophook:循环钩子,当前监听未返回false则一直执行

上述都有同步钩子,例如SyncHook

AsyncSeriesHook

异步串行钩子

异步并行钩子

16、同步钩子的使用


const { SyncHook } = require(\'tapable\')



let hook = new SyncHook([\'name\', \'age\'])



hook.tap(\'fn1\', function (name, age) {

console.log(\'fn1--->\', name, age)

})


hook.tap(\'fn2\', function (name, age) {

console.log(\'fn2--->\', name, age)

})


hook.call(\'zoe\', 18)//通过call方法调用





const { SyncBailHook } = require(\'tapable\')



let hook = new SyncBailHook([\'name\', \'age\'])



hook.tap(\'fn1\', function (name, age) {

console.log(\'fn1--->\', name, age)

})


hook.tap(\'fn2\', function (name, age) {

console.log(\'fn2--->\', name, age)

return undefined

})//返回undefined,fn3就不执行了


hook.tap(\'fn3\', function (name, age) {

console.log(\'fn3--->\', name, age)

})


hook.call(\'lg\', 100)



const { SyncWaterfallHook } = require(\'tapable\')



let hook = new SyncWaterfallHook([\'name\', \'age\'])



hook.tap(\'fn1\', function (name, age) {

console.log(\'fn1--->\', name, age)

return \'ret1\'

})


hook.tap(\'fn2\', function (name, age) {
//这里的name拿到了ret1

console.log(\'fn2--->\', name, age)

return \'ret2\'

})


hook.tap(\'fn3\', function (name, age) {

console.log(\'fn3--->\', name, age)

return \'ret3\'

})


hook.call(\'zce\', 33)



const { SyncLoopHook } = require(\'tapable\')



let hook = new SyncLoopHook([\'name\', \'age\'])



let count1 = 0

let count2 = 0

let count3 = 0



hook.tap(\'fn1\', function (name, age) {

console.log(\'fn1--->\', name, age)

if (++count1 === 1) {

count1 = 0

return undefined//如果不返回undefined,就会从头再循环一次

}
return true

})


hook.tap(\'fn2\', function (name, age) {

console.log(\'fn2--->\', name, age)

// if (++count2 === 2) {
// count2 = 0
// return undefined
// }
// return true
})


hook.tap(\'fn3\', function (name, age) {

console.log(\'fn3--->\', name, age)

})


hook.call(\'foo\', 100)

17、异步钩子


const { AsyncParallelHook } = require(\'tapable\')

//并行是指 等待所有并发的异步事件执行之后再执行最终的异步回调

let hook = new AsyncParallelHook([\'name\'])



// 对于异步钩子的使用,在添加事件监听时会存在三种方式: tap tapAsync tapPromise
// hook.tap(\'fn1\', function (name) {
// console.log(\'fn1--->\', name)
// })


// hook.tap(\'fn2\', function (name) {
// console.log(\'fn2--->\', name)
// })


// hook.callAsync(\'zoe\', function () {
// console.log(\'最后执行了回调操作\')
// })


/* console.time(\'time\')
hook.tapAsync(\'fn1\', function (name, callback) {
setTimeout(() => {
console.log(\'fn1--->\', name)
callback()
}, 1000)
})


hook.tapAsync(\'fn2\', function (name, callback) {
setTimeout(() => {
console.log(\'fn2--->\', name)
callback()
}, 2000)
})


hook.callAsync(\'lg\', function () {
console.log(\'最后一个回调执行了\')
console.timeEnd(\'time\')
}) */


// 03 promise
console.time(\'time\')
hook.tapPromise(\'fn1\', function (name) {

return new Promise(function (resolve, reject) {

setTimeout(() => {

console.log(\'fn1--->\', name)

resolve()
}, 1000)

})
})


hook.tapPromise(\'fn2\', function (name) {

return new Promise(function (resolve, reject) {

setTimeout(() => {

console.log(\'fn2--->\', name)

resolve()
}, 2000)

})
})


hook.promise(\'foo\').then(() => {

console.log(\'end执行了\')
console.timeEnd(\'time\')
})

//最终输出:






const { AsyncParallelBailHook } = require(\'tapable\')



let hook = new AsyncParallelBailHook([\'name\'])



console.time(\'time\')
hook.tapAsync(\'fn1\', function (name, callback) {

setTimeout(() => {

console.log(\'fn1--->\', name)

callback()
}, 1000)

})


hook.tapAsync(\'fn2\', function (name, callback) {

setTimeout(() => {

console.log(\'fn2--->\', name)

callback(\'err’)//这边就会提前结束hook.callAsync的执行
}, 2000)

})


hook.tapAsync(\'fn3\', function (name, callback) {

setTimeout(() => {

console.log(\'fn3--->\', name)

callback()
}, 3000)

})


hook.callAsync(\'zce\', function () {

console.log(\'最后的回调执行了\')
console.timeEnd(\'time\')
})


const { AsyncSeriesHook } = require(\'tapable\')


//异步的串行
let hook = new AsyncSeriesHook([\'name\'])



console.time(\'time\')
hook.tapPromise(\'fn1\', function (name) {

return new Promise((resolve, reject) => {

setTimeout(() => {

console.log(\'fn1--->\', name)

resolve()
}, 1000)

})
})


hook.tapPromise(\'fn2\', function (name) {

return new Promise((resolve, reject) => {

setTimeout(() => {

console.log(\'fn2--->\', name)

resolve()
}, 2000)

})
})


hook.promise(\'foo\').then(function () {

console.log(\'~~~~\')
console.timeEnd(\'time\')
})

19、手写异步钩子

const SyncHook = require(\'./SyncHook.js\')

let hook = new SyncHook([\'name\', \'age\'])

hook.tap(\'fn1\', function (name, age) {
console.log(\'fn1-->\', name, age)
})

hook.tap(\'fn2\', function (name, age) {
console.log(\'fn2-->\', name, age)
})

hook.call(\'zoe66\', 18)

/**

  • 01 实例化 hook , 定义 _x = [f1, f2, ...] taps = [{}, {}]
  • 02 实例调用 tap taps = [{}, {}]
  • 03 调用 call 方法, HookCodeFactory setup create
  • 04 Hook SyncHook HookCodeFactory
    */

let Hook = require(\'./Hook.js\')


class HookCodeFactory {
  args() {
    return this.options.args.join(\',\')  // ["name", "age"]===> name, age
  }
  head() {
    return `var _x = this._x;`
  }
  content() {
    let code = ``
    for (var i = 0; i < this.options.taps.length; i++) {
      code += `var _fn${i} = _x[${i}];_fn${i}(${this.args()});`
    }
    return code
  }
  setup(instance, options) {  // 先准备后续需要使用到的数据
    this.options = options  // 这里的操作在源码中是通过 init 方法实现,而我们当前是直接挂在了 this 身上
    instance._x = options.taps.map(o => o.fn)   // this._x = [f1, f2, ....]
  }
  create() { // 核心就是创建一段可执行的代码体然后返回
    let fn
    // fn = new Function("name, age", "var _x = this._x, var _fn0 = _x[0]; _fn0(name, age);")
    // 在content中拼接出对应的代码片段
    fn = new Function(
      this.args(),
      this.head() + this.content()
    )
    return fn
  }
}


let factory = new HookCodeFactory()


class SyncHook extends Hook {
  constructor(args) {
    super(args)
  }


  compile(options) {  // {taps: [{}, {}], args: [name, age]}
    factory.setup(this, options)
    return factory.create(options)
  }
}


module.exports = SyncHook

./Hook.js

class Hook {
  constructor(args = []) {
    this.args = args
    this.taps = []  // 将来用于存放组装好的 {}对象信息
    this._x = undefined  // 将来在代码工厂函数中会给 _x = [f1, f2, f3....]
  }


  tap(options, fn) {
    if (typeof options === \'string\') {
      options = { name: options }
    }
    options = Object.assign({ fn }, options)  // { fn:... name:fn1 }


    // 调用以下方法将组装好的 options 添加至this.taps
    this._insert(options)
  }


  _insert(options) {
    this.taps[this.taps.length] = options
  }


  call(...args) {
    // 01 创建将来要具体执行的函数代码结构
    let callFn = this._createCall()
    // 02 调用上述的函数(args传入进去)
    return callFn.apply(this, args)
  }


  _createCall() {
    return this.compile({
      taps: this.taps,
      args: this.args
    })
  }
}


module.exports = Hook

20、手写同步钩子


class Hook {
  constructor(args = []) {
    this.args = args
    this.taps = []  // 将来用于存放组装好的 {}
    this._x = undefined  // 将来在代码工厂函数中会给 _x = [f1, f2, f3....]
  }


  tap(options, fn) {
    if (typeof options === \'string\') {
      options = { name: options }
    }
    options = Object.assign({ fn }, options)  // { fn:... name:fn1 }


    // 调用以下方法将组装好的 options 添加至 []
    this._insert(options)
  }


  tapAsync(options, fn) {
    if (typeof options === \'string\') {
      options = { name: options }
    }
    options = Object.assign({ fn }, options)  // { fn:... name:fn1 }


    // 调用以下方法将组装好的 options 添加至 []
    this._insert(options)
  }


  _insert(options) {
    this.taps[this.taps.length] = options
  }


  call(...args) {
    // 01 创建将来要具体执行的函数代码结构
    let callFn = this._createCall()
    // 02 调用上述的函数(args传入进去)
    return callFn.apply(this, args)
  }


  callAsync(...args) {
    let callFn = this._createCall()
    return callFn.apply(this, args)
  }


  _createCall() {
    return this.compile({
      taps: this.taps,
      args: this.args
    })
  }
}


module.exports = Hook


let Hook = require(\'./Hook.js\')


class HookCodeFactory {
  args({ after, before } = {}) {
    let allArgs = this.options.args
    if (before) allArgs = [before].concat(allArgs)
    if (after) allArgs = allArgs.concat(after)
    return allArgs.join(\',\')  // ["name", "age"]===> name, age
  }
  head() {
    return `"use strict";var _context;var _x = this._x;`
  }
  content() {
    let code = `var _counter = ${this.options.taps.length};var _done = (function () {
      _callback();
    });`
    for (var i = 0; i < this.options.taps.length; i++) {
      code += `var _fn${i} = _x[${i}];_fn${i}(name, age, (function () {
        if (--_counter === 0) _done();
      }));`
    }
    return code
  }
  setup(instance, options) {  // 先准备后续需要使用到的数据
    this.options = options  // 这里的操作在源码中是通过 init 方法实现,而我们当前是直接挂在了 this 身上
    instance._x = options.taps.map(o => o.fn)   // this._x = [f1, f2, ....]
  }
  create() { // 核心就是创建一段可执行的代码体然后返回
    let fn
    // fn = new Function("name, age", "var _x = this._x, var _fn0 = _x[0]; _fn0(name, age);")
    fn = new Function(
      this.args({ after: \'_callback\' }),
      this.head() + this.content()
    )
    return fn
  }
}


let factory = new HookCodeFactory()


class AsyncParallelHook extends Hook {
  constructor(args) {
    super(args)
  }


  compile(options) {  // {taps: [{}, {}], args: [name, age]}
    factory.setup(this, options)
    return factory.create(options)
  }
}


module.exports = AsyncParallelHook

以上是关于webpack源码分析2的主要内容,如果未能解决你的问题,请参考以下文章

前端工程化9:Webpack构建流程分析,Webpack5源码解读

Webpack-源码三,从源码分析如何写一个plugin

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

来了来了,最新vite源码分析,vite到底为什么比webpack快

webpack源码分析1