webpack tapable 用法详解

尝试了解webpack的工作机制的时候,发现webpack大量使用了tapable这个核心库来组织代码,
tapable 提供了很多中钩子注册,执行的机制。让webpack可以灵活的管理模块编译的各个阶段,灵活的在处理的不同阶段触发plugin的预先定义的钩子。
仓库地址:github
github readme里有详细的使用文档

本文通过自己使用的demo详细讲解

一:tapable 提供的常规的N种钩子

const {
     
	SyncHook,
	SyncBailHook,
	SyncWaterfallHook,
	SyncLoopHook,
	AsyncParallelHook,
	AsyncParallelBailHook,
	AsyncSeriesHook,
	AsyncSeriesBailHook,
	AsyncSeriesWaterfallHook
 } = require("tapable");

先用一个最简单的demo来感受下,使用方法是:

let ahook  = new SyncHook() // 通过new 创建一个钩子的实例
ahook.tap('name', () => {
     console.log('xxx')}) // 通过tap来挂载一个函数到钩子实例上
ahook.call() // 通过call来触发钩子函数执行,钩子执行会触发所有挂载函数的执行
// xxx 调用call后,输出xxx

这是最简单的一个执行流程,不同的钩子,声明,挂载,执行机制,会有 差异,

下面将写一个完整的类来定义所有的钩子和钩子对应的触发方法,然后一一讲解

 class LearnTabpable {
     
     constructor () {
     
         this.hooks = {
     
             testSyncHook: new SyncHook(),
             testSyncBailHook: new SyncBailHook(),
             testSyncWaterfallHook: new SyncWaterfallHook(['wa']),
             testSyncLoopHook: new SyncLoopHook(),
             testAsyncParalleHook: new AsyncParallelHook(),
             testAsyncParalleBailHook: new AsyncParallelBailHook(),
             testAsyncSeriesHook: new AsyncSeriesHook(),
             testAsyncSeriesBailHook: new AsyncSeriesBailHook(),
             testAsyncSeriesWaterfallHook: new AsyncSeriesWaterfallHook(['aa'])
         }
     }
 }

var a = new LearnTabpable()

  1. SyncHook 也叫同步钩子,通过tap勾住synchook钩子,通过call 执行钩子
 a.hooks.testSyncHook.tap('test sync hook', () => {
     console.log('test sync hook console1')})
 a.hooks.testSyncHook.tap('test sync hook', () => {
     console.log('test sync hook console2')})
a.hooks.testSyncHook.call()

运行结果

test sync hook console1
test sync hook console2
  1. SyncBailHook 同步条件钩子
 a.hooks.testSyncBailHook.tap('test ', () => {
     console.log('test sync bail hook console')})
 a.hooks.testSyncBailHook.tap('test ', () => {
     console.log('return 1'); return 1}) 
 // return 一个非undefined的内容,就会停止
 a.hooks.testSyncBailHook.tap('test ', () => {
     console.log('test sync bail hook console2')})
 a.hooks.testSyncBailHook.call()

同步钩子函数上的挂载函数按顺序执行,当某一个函数返回了一个非undefined的值的时候,回停止向下继续执行
执行结果

test sync bail hook console
return 1
  1. SyncWaterfallHook 同步瀑布钩子
 a.hooks.testSyncWaterfallHook.tap('f', (water) => {
     
 	console.log(water);
  	return water + 1
 })
 a.hooks.testSyncWaterfallHook.tap('f', (water) => {
     
     console.log(water);
     return water + 1
  })
 a.hooks.testSyncWaterfallHook.tap('f', (water) => {
     
    console.log(water)
 })
 a.hooks.testSyncWaterfallHook.call(10)

10 将作为参数在挂载函数中作为入口流转因子,再接下来的挂载函数中,会将上一次的返回值作为新的流转因子进行流转。
执行结果

10
11
12
  1. SyncLoopHook 同步循环钩子
 let index = 0
 a.hooks.testSyncLoopHook.tap('f', () => {
     
    console.log(index)
    if (index < 5) {
     
        index++
        return index
    }
 })

 a.hooks.testSyncLoopHook.tap('f', () => {
     
    console.log('结束了')
 })
a.hooks.testSyncLoopHook.call()

此钩子会不停的触发挂载函数,直到挂载函数返回内容为undefined的时候,继续执行下一个挂载函数。直到结束

0
1
2
3
4
5 
结束了
  1. AsyncParallelHook 异步并行钩子
    会并发执行两个异步的挂载函数,等两个都执行结束了,钩子函数执行自己的callback
a.hooks.testAsyncParalleHook.tapAsync('f', (callback) => {
     
    setTimeout(() => {
     
        console.log('p1')
        callback()
    }, 1000);
})
a.hooks.testAsyncParalleHook.tapAsync('f2', (callback) => {
     
    setTimeout(() => {
     
        console.log('p2')
        callback()
    }, 2000);
})
a.hooks.testAsyncParalleHook.callAsync(() => {
     
    console.log('并行走结束了')
})

运行结果

p1
p2
并行走结束了
  1. AsyncParallelBailHook 异步并行条件钩子
    两个挂载的异步函数虽然是异步执行的,但是只要有一个callback 传入一个非undefined的值,钩子函数便会执行自己的callback,还没有执行完成的挂载还是会接着走,不会被停止
a.hooks.testAsyncParalleBailHook.tapAsync('f', (callback) => {
     
  setTimeout(() => {
     
    console.log('delay 1')
    callback(1)// 这里会导致直接触发钩子函数的回调函数
  }, 1000);
})

a.hooks.testAsyncParalleBailHook.tapAsync('f1', (callback) => {
     
    setTimeout(() => {
     
      console.log('delay 2')
      callback(1)  
    }, 2000);
})
a.hooks.testAsyncParalleBailHook.callAsync((result) => {
     
    console.log(result)
})

运行结果

delay1
1
delay2
  1. AsyncSeriesHook 异步串行钩子
    如下两个异步函数将串行执行,第一个挂载函数成功后,才开始执行第二个挂载函数
a.hooks.testAsyncSeriesHook.tapPromise('f1', () => {
     
    return new Promise((resolve, reject) => {
     
        setTimeout(() => {
     
            console.log('p1')
            resolve()
        }, 1000);
    })
})
a.hooks.testAsyncSeriesHook.tapPromise('f2', () => {
     
    return new Promise((resolve, reject) => {
     
        setTimeout(() => {
     
            console.log('p2')
            resolve()
        }, 2000);
    })
})
a.hooks.testAsyncSeriesHook.promise().then(()=> {
     
    console.log('p3')
})

运行结果

p1 // 1秒后
p2 // 3秒后
p3 // p2 执行完成立即触发
  1. AsyncSeriesBailHook 异步串行条件钩子
    钩子函数的异步挂载函数按顺序执行,当第一个执行的resolve 返回一个非undefined的值的时候,后面的串行异步挂载函数将不执行,钩子的回调函数将立马执行。
a.hooks.testAsyncSeriesBailHook.tapPromise('f1', () => {
     
    return new Promise((resolve, reject) => {
     
        setTimeout(() => {
     
            console.log('pB1')
            resolve(1)
        }, 1000);
    })
})
a.hooks.testAsyncSeriesBailHook.tapPromise('f2', () => {
     
    return new Promise((resolve, reject) => {
     
        setTimeout(() => {
     
            console.log('pB2')
            resolve()
        }, 2000);
    })
})
a.callTestAsyncSeriesBailHook().then(()=> {
     
    console.log('pB3')
})

运行结果

pB1 // 1秒后运行
pB3
  1. AsyncSeriesWaterfallHook 异步串行瀑布钩子
// 串行参数传递
a.hooks.testAsyncSeriesWaterfallHook.tapPromise('f1', (result) => {
     
    return new Promise((resolve, reject) => {
     
        setTimeout(() => {
     
            console.log('wf1' , result)
            resolve('param 1')
        }, 1000);
    })
})
a.hooks.testAsyncSeriesWaterfallHook.tapPromise('f2', (result) => {
     
    return new Promise((resolve, reject) => {
     
        setTimeout(() => {
     
            console.log('wf2', result)
            resolve('xxxx')
        }, 2000);
    })
})
a.hooks.testAsyncSeriesWaterfallHook.promise('param 0').then((result)=> {
     
    console.log('wfcb', result)
})

运行结果

wf1 param 0
wf2 param 1
wfcb xxxx

二:tapable 其他的API

  1. HookMap
    通过该构造函数能够构造一个可以快速创建 读取 hook的map结构,方便调用和管理
const keyedHook = new HookMap(key => new SyncHook(["arg"]))
keyedHook.for("some-key").tap("MyPlugin", (arg) => {
      /* ... */ });
keyedHook.for("some-key").tapAsync("MyPlugin", (arg, callback) => {
      /* ... */ });
keyedHook.for("some-key").tapPromise("MyPlugin", (arg) => {
      /* ... */ });
const hook = keyedHook.get("some-key");
if(hook !== undefined) {
     
	hook.callAsync("arg", err => {
      /* ... */ });
}

2.MultiHook
我们可以基于MultiHook来创建一个新的hook,这个hook tap后,会同时挂载在两个hook上

const {
      MultiHook } = require("tapable");
this.hooks.allHooks = new MultiHook([this.hooks.hookA, this.hooks.hookB]);
this.hooks.allHooks.tap('f', () => {
     })

你可能感兴趣的:(前端,webpack)