webpack插件机制之Tapable
栏目: JavaScript · 发布时间: 5年前
内容简介:对于Webpack有一句话Tapable提供了很多类型的hook,分为同步和异步两大类(异步中又区分异步并行和异步串行),而根据事件执行的终止条件的不同,由衍生出 Bail/Waterfall/Loop 类型。下图展示了每种类型的作用:
对于Webpack有一句话 Everything is a plugin ,Webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable。Tapable有点类似nodejs的events库,核心原理也是依赖与发布订阅模式。webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation都是Tapable的实例。下面介绍一下tapable的用法和原理。
const { SyncHook, SyncBailHook, SyncWaterfallHook, SyncLoopHook, AsyncParallelHook, AsyncParallelBailHook, AsyncSeriesHook, AsyncSeriesBailHook, AsyncSeriesWaterfallHook } = require("tapable"); 复制代码
Tapable Hook概览
Tapable提供了很多类型的hook,分为同步和异步两大类(异步中又区分异步并行和异步串行),而根据事件执行的终止条件的不同,由衍生出 Bail/Waterfall/Loop 类型。
下图展示了每种类型的作用:
- BasicHook: 执行每一个,不关心函数的返回值,有 SyncHook、AsyncParallelHook、AsyncSeriesHook。
- BailHook: 顺序执行 Hook,遇到第一个结果 result !== undefined 则返回,不再继续执行。有:SyncBailHook、AsyncSeriseBailHook, AsyncParallelBailHook。
- WaterfallHook: 类似于 reduce,如果前一个 Hook 函数的结果 result !== undefined,则 result 会作为后一个 Hook 函数的第一个参数。既然是顺序执行,那么就只有 Sync 和 AsyncSeries 类中提供这个Hook:SyncWaterfallHook,AsyncSeriesWaterfallHook
- LoopHook: 不停的循环执行 Hook,直到所有函数结果 result === undefined。同样的,由于对串行性有依赖,所以只有 SyncLoopHook 和 AsyncSeriseLoopHook (PS:暂时没看到具体使用 Case)
SyncHook的用法及实现
Sync为同步串行的执行关系,用法如下:
let { SyncHook } = require("tapable"); class Lesson { constructor() { this.hooks = { arch: new SyncHook(["name"]) }; } // 注册监听函数 tap() { this.hooks.arch.tap("node", function(name) { console.log("node", name); }); this.hooks.arch.tap("react", function(name) { console.log("react", name); }); } start() { this.hooks.arch.call("musion"); } } let l = new Lesson(); // 注册这两个事件 l.tap(); // 启动钩子 l.start(); /** * 打印出来的值为: * node musion * react musion */ 复制代码
SyncHook是一个很典型的通过发布订阅方式实现的,实现方式如下:
// 钩子是同步的 class SyncHook { // args => ["name"] constructor() { this.tasks = []; } tap(name, task) { this.tasks.push(task); } call(...args) { this.tasks.forEach(task => task(...args)); } } let hook = new SyncHook(["name"]); hook.tap("react", function(name) { console.log("react", name); }); hook.tap("node", function(name) { console.log("node", name); }); hook.call("musion"); /** * 打印出来的值为: * node musion * react musion */ 复制代码
SyncBailHook的用法及实现
SyncBailHook为同步串行的执行关系,只要监听函数中有一个函数的返回值不为 null,则跳过剩下所有的逻辑,用法如下:
let { SyncBailHook } = require("tapable"); class Lesson { constructor() { this.hooks = { arch: new SyncBailHook(["name"]) }; } // 注册监听函数 tap() { this.hooks.arch.tap("node", function(name) { console.log("node", name); //return "stop"; return undefined; }); this.hooks.arch.tap("react", function(name) { console.log("react", name); }); } start() { this.hooks.arch.call("musion"); } } let l = new Lesson(); // 注册这两个事件 l.tap(); // 启动钩子 l.start(); /** * 打印出来的值为: * node musion * react musion */ 复制代码
SyncBailHook的实现:
// 钩子是同步的,bail -> 保险 class SyncBailHook { // args => ["name"] constructor() { this.tasks = []; } tap(name, task) { this.tasks.push(task); } call(...args) { // 当前函数的返回值 let ret; // 当前要先执行第一个 let index = 0; do { ret = this.tasks[index++](...args); } while (ret === undefined && index < this.tasks.length); } } let hook = new SyncBailHook(["name"]); hook.tap("react", function(name) { console.log("react", name); return "stop"; }); hook.tap("node", function(name) { console.log("node", name); }); hook.call("musion"); /** * 打印出来的值为: * node musion * react musion */ 复制代码
SyncWaterfallHook的用法及实现
SyncWaterfallHook为同步串行的执行关系,上一个监听函数的返回值可以传给下一个监听函数,用法如下:
let { SyncWaterfallHook } = require("tapable"); // waterfall 瀑布 上面会影响下面的 class Lesson { constructor() { this.hooks = { arch: new SyncWaterfallHook(["name"]) }; } // 注册监听函数 tap() { this.hooks.arch.tap("node", function(name) { console.log("node", name); return "node学得还不错"; }); this.hooks.arch.tap("react", function(data) { console.log("react", data); }); } start() { this.hooks.arch.call("musion"); } } let l = new Lesson(); // 注册这两个事件 l.tap(); // 启动钩子 l.start(); /** * 打印出来的值为: * node musion * react node学得还不错 */ 复制代码
SyncWaterfallHook的实现:
// 钩子是同步的 class SyncWaterfallHook { // args => ["name"] constructor() { this.tasks = []; } tap(name, task) { this.tasks.push(task); } call(...args) { let [first, ...others] = this.tasks; let ret = first(...args); others.reduce((a, b) => { return b(a); }, ret); } } let hook = new SyncWaterfallHook(["name"]); hook.tap("react", function(name) { console.log("react", name); return "react ok"; }); hook.tap("node", function(data) { console.log("node", data); return "node ok"; }); hook.tap("webpack", function(data) { console.log("webpack", data); }); hook.call("musion"); /** * 打印出来的值为: * react musion * node react ok * webpack node ok */ 复制代码
SyncLoopHook的用法及实现
SyncLoopHook为同步循环的执行关系,当监听函数被触发的时候,如果该监听函数返回true时则这个监听函数会反复执行,如果返回 undefined 则表示退出循环,用法如下:
let { SyncLoopHook } = require("tapable"); // 同步遇到某个不返回undefined的监听函数会多次执行 class Lesson { constructor() { this.index = 0; this.hooks = { arch: new SyncLoopHook(["name"]) }; } // 注册监听函数 tap() { this.hooks.arch.tap("node", name => { console.log("node", name); return ++this.index === 3 ? undefined : "继续学"; }); this.hooks.arch.tap("react", data => { console.log("react", data); }); } start() { this.hooks.arch.call("musion"); } } let l = new Lesson(); // 注册这两个事件 l.tap(); // 启动钩子 l.start(); /** * 打印出来的值为: * node musion * node musion * node musion * react musion */ 复制代码
SyncLoopHook的实现:
// 钩子是同步的 class SyncLoopHook { // args => ["name"] constructor() { this.tasks = []; } tap(name, task) { this.tasks.push(task); } call(...args) { this.tasks.forEach(task => { let ret; do { ret = task(...args); } while (ret != undefined); }); } } let hook = new SyncLoopHook(["name"]); let total = 0; hook.tap("react", function(name) { console.log("react", name); return ++total === 3 ? undefined : "继续学"; }); hook.tap("node", function(data) { console.log("node", data); }); hook.tap("webpack", function(data) { console.log("webpack", data); }); hook.call("musion"); /** * 打印出来的值为: * react musion * react musion * react musion * node musion * webpack musion */ 复制代码
AsyncParallelHook的用法及实现
AsyncParallelHook为异步并发的执行关系,用法如下:
let { AsyncParallelHook } = require("tapable"); // 异步的钩子分为串行和并行 // 串行:第一个异步执行完,才会执行第二个 // 并行:需要等待所有并发的异步事件执行后再执行回调方法 // 注册方法: tap注册 tapAsync注册 class Lesson { constructor() { this.hooks = { arch: new AsyncParallelHook(["name"]) }; } // 注册监听函数 tap() { this.hooks.arch.tapAsync("node", (name, cb) => { setTimeout(() => { console.log("node", name); cb(); }, 1000); }); this.hooks.arch.tapAsync("react", (name, cb) => { setTimeout(() => { console.log("react", name); cb(); }, 1000); }); } start() { this.hooks.arch.callAsync("musion", function() { console.log("end"); }); } } let l = new Lesson(); // 注册这两个事件 l.tap(); // 启动钩子 l.start(); /** * 打印出来的值为: * node musion * react musion * end */ 复制代码
AsyncParallelHook的实现:
class SyncParralleHook { constructor() { this.tasks = []; } tapAsync(name, task) { this.tasks.push(task); } callAsync(...args) { // 拿出最终的函数 let finalCallBack = args.pop(); let index = 0; // 类似Promise.all let done = () => { index++; if (index === this.tasks.length) { finalCallBack(); } }; this.tasks.forEach(task => { task(...args, done); }); } } let hook = new SyncParralleHook(["name"]); hook.tapAsync("react", function(name, cb) { setTimeout(() => { console.log("react", name); cb(); }, 1000); }); hook.tapAsync("node", function(name, cb) { setTimeout(() => { console.log("node", name); cb(); }, 1000); }); hook.callAsync("musion", function() { console.log("end"); }); /** * 打印出来的值为: * react musion * react musion * react musion * node musion * webpack musion */ 复制代码
AsyncSeriesHook的用法及实现
AsyncSeriesHook为异步串行的执行关系,用法如下:
// AsyncSeriesHook 异步串行 let { AsyncSeriesHook } = require("tapable"); class Lesson { constructor() { this.hooks = { arch: new AsyncSeriesHook(["name"]) }; } // 注册监听函数 tap() { this.hooks.arch.tapAsync("node", (name, cb) => { setTimeout(() => { console.log("node", name); cb(); }, 4000); }); this.hooks.arch.tapAsync("react", (name, cb) => { setTimeout(() => { console.log("react", name); cb(); }, 1000); }); } start() { this.hooks.arch.callAsync("musion", function() { console.log("end"); }); } } let l = new Lesson(); // 注册这两个事件 l.tap(); // 启动钩子 l.start(); /** * 打印出来的值为: * node musion * react musion * end */ 复制代码
AsyncSeriesHook的实现:
class SyncSeriesHook { constructor() { this.tasks = []; } tapAsync(name, task) { this.tasks.push(task); } callAsync(...args) { let finalCallback = args.pop(); let index = 0; let next = () => { if (this.tasks.length === index) return finalCallback(); let task = this.tasks[index++]; task(...args, next); }; next(); } } 复制代码
AsyncSeriesWaterfallHook的用法及实现
AsyncSeriesWaterfallHook为异步串行的执行关系,上一个监听函数的中的callback(err, data)的第二个参数,可以作为下一个监听函数的参数,用法如下:
class SyncSeriesWaterfallHook { constructor() { this.tasks = []; } tapAsync(name, task) { this.tasks.push(task); } callAsync(...args) { let finalCallback = args.pop(); let index = 0; let next = (err, data) => { let task = this.tasks[index]; if (!task) return finalCallback(); // 执行的是第一个 if (index === 0) { task(...args, next); } else { task(data, next); } index++; }; next(); } } let hook = new SyncSeriesWaterfallHook(["name"]); hook.tapAsync("react", function(name, cb) { setTimeout(() => { console.log("react", name); cb(null, "musion"); }, 3000); }); hook.tapAsync("node", function(name, cb) { setTimeout(() => { console.log("node", name); cb(null); }, 1000); }); hook.callAsync("musion", function() { console.log("end"); }); /** * 打印出来的值为: * node musion * end */ 复制代码
AsyncSeriesWaterfallHook的实现:
class SyncSeriesWaterfallHook { constructor() { this.tasks = []; } tapAsync(name, task) { this.tasks.push(task); } callAsync(...args) { let finalCallback = args.pop(); let index = 0; let next = (err, data) => { let task = this.tasks[index]; if (!task) return finalCallback(); // 执行的是第一个 if (index === 0) { task(...args, next); } else { task(data, next); } index++; }; next(); } } 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Python插件机制实现
- osquery插件机制简介
- webpack系列-插件机制杂记
- SOFATracer 插件埋点机制详解
- Istio 服务注册插件机制代码解析
- Istio 服务注册插件机制代码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
网站转换率优化之道
[美] Khalid Saleh、[美] Ayat Shukairy / 顾 毅 / 人民邮电出版社 / 2012-4 / 45.00元
内容简介: 怎样才能将访问者转化为顾客? 本书提供了一些切实可行的建议,比如如何说服访问者作出购买决定,如何避免用户因信息过量或导航繁琐而离开网站等。不论你是在设计或营销大型电子商务网站,还是在管理中小型在线业务,都可以从本书学会怎样使用市场营销原则、设计方法、可用性原则和分析数据来持续提升网站的转换率。 作者帮助过众多公司吸引在线顾客,有着丰富的实战经验,在书中细致讨论了从访问......一起来看看 《网站转换率优化之道》 这本书的介绍吧!