Webpack本质上是一种时间流的机制,它的工作流程就是各个插件出阿联起来,实现这一切的核心就是Tapable,Tapable优点类似于nodejs的events库,核心原理也是依赖于发布订阅模式

  1. const {
  2. SyncHook,
  3. SyncBailHook,
  4. SyncWaterfailHook,
  5. SyncloopHook,
  6. ...
  7. } = require('tapable')

1、使用tapable

  1. let { SyncHook } = require("tapable");
  2. class Lesson {
  3. constructor() {
  4. this.hooks = {
  5. arch: new SyncHook(["name"]),
  6. };
  7. }
  8. tap() {
  9. console.log("注册监听函数");
  10. // 注册监听函数
  11. this.hooks.arch.tap("node", function (name) {
  12. console.log("node", name);
  13. });
  14. this.hooks.arch.tap("react", function (name) {
  15. console.log("react", name);
  16. });
  17. }
  18. start() {
  19. console.log("开始");
  20. this.hooks.arch.call("rock");
  21. }
  22. }
  23. let l = new Lesson();
  24. l.tap(); // 注册事件
  25. l.start(); // 启动钩子

2、手写tapable

1、SyncHook

同步钩子 , 调用 tap 方法收集依赖,调用 call 方法进行执行

  1. class SyncHook {
  2. constructor(config) {
  3. this.config = config;
  4. this.tasks = [];
  5. }
  6. tap(name, fn) {
  7. this.tasks.push(fn);
  8. }
  9. call(...args) {
  10. this.tasks.forEach((el) => el(...args));
  11. }
  12. }

2、SyncBailHook

可中断的钩子

同步钩子 , 调用 tap 方法收集依赖,调用 call 方法进行执行

当上一个函数返回非 undefined,停止后面的调用,为 undefined 时,继续执行

  1. class SyncBailHook {
  2. constructor(config) {
  3. this.config = config;
  4. this.tasks = [];
  5. }
  6. tap(name, fn) {
  7. this.tasks.push(fn);
  8. }
  9. call(...args) {
  10. for (let i = 0; i < this.tasks.length; i++) {
  11. let ret = this.tasks[i](...args);
  12. if (ret === undefined) break;
  13. }
  14. }
  15. }

3、SyncWaterfallHook

具有执行顺序的同步钩子

同步钩子 , 调用 tap 方法收集依赖,调用 call 方法进行执行

当上一个函数返回 undefined 或者不返回东西时,正常执行传参, 当返回非 undefined 时,将上一个函数的结果传给下一个函数

  1. class SyncWaterfallHook {
  2. constructor(config) {
  3. this.config = config;
  4. this.tasks = [];
  5. }
  6. tap(name, fn) {
  7. this.tasks.push(fn);
  8. }
  9. call(...args) {
  10. let ret;
  11. for (let i = 0; i < this.tasks.length; i++) {
  12. ret = this.tasks[i](...args);
  13. if (ret !== undefined) {
  14. ret = this.tasks[i](ret);
  15. }
  16. }
  17. // let [ first, ...others ] = this.tasks;
  18. // let ret = first(...args);
  19. // others.reduce((a,b)=>{
  20. // return b(a)
  21. // },ret)
  22. }
  23. }

4、SyncLoopHook

具有执行顺序的同步带循环的钩子

同步钩子 , 调用 tap 方法收集依赖,调用 call 方法进行执行

当执行的函数返回值不是 undefined 时,会循环执行这个函数,直到返回 undefined 时,才执行下一个函数

  1. class SyncLoopHook {
  2. constructor(config) {
  3. this.config = config;
  4. this.tasks = [];
  5. }
  6. tap(name, fn) {
  7. this.tasks.push(fn);
  8. }
  9. call(...args) {
  10. // 方法一:建一个索引 依次执行每一个函数 当函数执行后返回是undefined时,i++
  11. let ret;
  12. for (let i = 0; i < this.tasks.length; ) {
  13. ret = this.tasks[i](...args);
  14. if (ret === undefined) {
  15. i++;
  16. } else {
  17. ret = this.tasks[i](...args);
  18. }
  19. }
  20. // 方法二
  21. // this.tasks.forEach((task) => {
  22. // let ret;
  23. // do {
  24. // ret = task(...args);
  25. // } while (ret !== undefined);
  26. // });
  27. }
  28. }

5、AsyncParallelHook

异步并行的钩子

调用 tapAsync 注册异步事件,当事件中的 cb 都执行后,调用 callAsync 执行最后的返回结束事件

思路 执行每一个异步任务,具有一个回调的函数,执行回调函数然后 index+1,当 index===一共的异步任务时执行 callAsync

  1. class AsyncParallelHook {
  2. constructor(config) {
  3. this.config = config;
  4. this.tasks = [];
  5. }
  6. tapAsync(name, fn) {
  7. this.tasks.push(fn);
  8. }
  9. callAsync(args, doneFn) {
  10. let index = 0;
  11. // 执行每一个异步任务,具有一个回调的函数,执行回调函数然后index+1,当index===一共的异步任务时执行callAsync
  12. for (let i = 0; i < this.tasks.length; i++) {
  13. this.tasks[i](args, () => {
  14. index++;
  15. if (index === this.tasks.length) {
  16. doneFn();
  17. }
  18. });
  19. }
  20. }
  21. }

6、AsyncParallelPromiseHook

异步并行的钩子 支持 promise 写法

调用 tapPromise 注册异步事件,当事件中的 cb 都执行后,调用 promise .then 支持链式

  1. class AsyncParallePromiseHook {
  2. constructor(config) {
  3. this.config = config;
  4. this.tasks = [];
  5. }
  6. tapPromise(name, fn) {
  7. this.tasks.push(fn);
  8. }
  9. promise(args) {
  10. let tasks = this.tasks.map((task) => task(...args));
  11. return Promise.all(tasks);
  12. }
  13. }

7、AsyncSeriesHook

异步串行钩子

一个注册的函数执行完在执行下一个函数, 这种需要通过中间函数,递归的方式进行, 当 index 和函数的数目相同时停止递归

  1. class AsyncSeriesHook {
  2. constructor(config) {
  3. this.config = config;
  4. this.tasks = [];
  5. }
  6. tapAsync(name, fn) {
  7. this.tasks.push(fn);
  8. }
  9. callAsync(...args) {
  10. // 异步中间间 需要中间函数
  11. let index = 0;
  12. let finallyCallback = args.pop();
  13. let next = () => {
  14. if (index === this.tasks.length) return finallyCallback();
  15. this.tasks[index++](...args, next);
  16. };
  17. next();
  18. }
  19. }

支持 promise

  1. class AsyncSeriesHook {
  2. constructor(config) {
  3. this.config = config;
  4. this.tasks = [];
  5. }
  6. tapAsync(name, fn) {
  7. this.tasks.push(fn);
  8. }
  9. callAsync(...args) {
  10. // 异步中间间 需要中间函数
  11. let index = 0;
  12. let finallyCallback = args.pop();
  13. let next = () => {
  14. if (index === this.tasks.length) return finallyCallback();
  15. this.tasks[index++](...args, next);
  16. };
  17. next();
  18. }
  19. tapPromise(name, fn) {
  20. this.tasks.push(fn);
  21. }
  22. promise(...args) {
  23. // 异步串行 这种用跌掉器 reduce
  24. let [firstFun, ...others] = this.tasks;
  25. let ret = firstFun(...args);
  26. return others.reduce((p, n) => {
  27. return p.then(() => n(...args));
  28. }, ret);
  29. }
  30. }

8、AsyncSeriesWaterfallHook

  1. /**
  2. * AsyncSeriesHook
  3. * 异步串行钩子
  4. * 根express中间件很像,一个执行完 另一个再执行 依赖next函数写递归
  5. */
  6. class AsyncSeriesWaterfallHook {
  7. // 异步并行钩子
  8. constructor() {
  9. this.tasks = [];
  10. }
  11. tapAsync(name, task) {
  12. this.tasks.push(task);
  13. }
  14. callAsync(...args) { // tapPromise + promise
  15. // 异步迭代 需要一个中间函数
  16. let index = 0;
  17. let finalCallback = args.pop();
  18. let next = (err, data) => {
  19. // if(this.tasks.length === index) return finalCallback()
  20. let task = this.tasks[index];
  21. if (!task) return finalCallback();
  22. if (index === 0) { // 执行的是第一个函数
  23. task(...args, next);
  24. } else {
  25. task(data, next);
  26. }
  27. index++;
  28. };
  29. next();
  30. }
  31. }
  32. let hooks = new AsyncSeriesWaterfallHook(["name"]);
  33. hooks.tapAsync("react", (name, cb) => {
  34. setTimeout(() => {
  35. console.log("react", name);
  36. cb(null, "结果");
  37. }, 1000);
  38. });
  39. hooks.tapAsync("node", (name, cb) => {
  40. setTimeout(() => {
  41. console.log("node", name);
  42. cb(null, "结果");
  43. }, 1000);
  44. });
  45. // 等都执行完 在执行下面函数
  46. hooks.callAsync("rock", function () {
  47. console.log("ending");
  48. });