First published at: github.com/USTB-musion…
Tapable
There is a phrase for Webpack: Everything is a plugin. Webpack is essentially an event flow mechanism, and its workflow is to connect various plug-ins in series, and the core of all this is Tapable. Tapable is a bit like NodeJS ‘Events library, with a publish-subscribe model at its core. The core Compiler in Webpack, and the Compilation that creates bundles, are instances of Tapable. Here is the usage and principle of tapable. The code for the following example is originally at github.com/USTB-musion…
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
Copy the code
An overview of the Tapable Hook
Tapable provides many types of hook, which can be divided into synchronous and asynchronous (asynchronous parallel and asynchronous serial are distinguished in asynchronous). According to different termination conditions of event execution, the Bail/Waterfall/Loop type is derived.
The following figure shows the role of each type:
- BasicHook: Executes each one, regardless of the return value of the function. There are SyncHook, AsyncParallelHook, AsyncSeriesHook.
- BailHook: Execute the hooks sequentially, and the first result is result! == undefined returns, and the execution is not continued. There are SyncBailHook, AsyncSeriseBailHook, and AsyncParallelBailHook.
- WaterfallHook: Similar to reduce, if the result of the previous Hook function is result! If == undefined, result will be the first argument to the next Hook function. Since the execution is sequential, only Sync and AsyncSeries classes provide the Hook: SyncWaterfallHook, AsyncSeriesWaterfallHook
- LoopHook: execute Hook repeatedly until all functions result === undefined. Also, there are only syncloophooks and asyncseriseloophooks because of the serialization dependency.
Usage and implementation of SyncHook
Sync indicates the serial execution of synchronization. The usage is as follows:
let { SyncHook } = require("tapable");
class Lesson {
constructor() {
this.hooks = {
arch: new SyncHook(["name"])}; }// Register the listener function
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();
// Register both events
l.tap();
// Start the hook
l.start();
/** * The printed value is: * node musion * react musion */
Copy the code
SyncHook is a typical publish-subscribe implementation, implemented as follows:
// The hook is synchronized
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");
/** * The printed value is: * node musion * react musion */
Copy the code
SyncBailHook usage and implementation
SyncBailHook is a synchronous serial execution relationship. If one of the listener functions does not return null, the rest of the logic is skipped.
let { SyncBailHook } = require("tapable");
class Lesson {
constructor() {
this.hooks = {
arch: new SyncBailHook(["name"])}; }// Register the listener function
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();
// Register both events
l.tap();
// Start the hook
l.start();
/** * The printed value is: * node musion * react musion */
Copy the code
Implementation of SyncBailHook:
// Hook is synchronized, Bail -> insurance
class SyncBailHook {
// args => ["name"]
constructor() {
this.tasks = [];
}
tap(name, task) {
this.tasks.push(task);
}
call(. args) {
// The return value of the current function
let ret;
// Execute the first one first
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");
/** * The printed value is: * node musion * react musion */
Copy the code
SyncWaterfallHook usage and implementation
SyncWaterfallHook is a synchronous serial execution relationship. The return value of the previous listener can be passed to the next listener as follows:
let { SyncWaterfallHook } = require("tapable");
// The top of the waterfall affects the bottom
class Lesson {
constructor() {
this.hooks = {
arch: new SyncWaterfallHook(["name"])}; }// Register the listener function
tap() {
this.hooks.arch.tap("node".function(name) {
console.log("node", name);
return "Node is doing well.";
});
this.hooks.arch.tap("react".function(data) {
console.log("react", data);
});
}
start() {
this.hooks.arch.call("musion"); }}let l = new Lesson();
// Register both events
l.tap();
// Start the hook
l.start();
/** * The printable value is: * node musion * React node learns well */
Copy the code
Implementation of SyncWaterfallHook:
// The hook is synchronized
class SyncWaterfallHook {
// args => ["name"]
constructor() {
this.tasks = [];
}
tap(name, task) {
this.tasks.push(task);
}
call(. args) {
let [first, ...others] = this.tasks;
letret = first(... args); others.reduce((a, b) = > {
returnb(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");
/** * The printed value is: * react musion * node react OK * webpack node ok */
Copy the code
Usage and implementation of SyncLoopHook
SyncLoopHook is an execution relation of a synchronous loop. When a listener is triggered, it will be executed repeatedly if the listener returns true. If undefined, it will exit the loop.
let { SyncLoopHook } = require("tapable");
// Synchronization executes multiple times when it encounters a listener that does not return undefined
class Lesson {
constructor() {
this.index = 0;
this.hooks = {
arch: new SyncLoopHook(["name"])}; }// Register the listener function
tap() {
this.hooks.arch.tap("node".name= > {
console.log("node", name);
return ++this.index === 3 ? undefined : "Continue learning";
});
this.hooks.arch.tap("react".data= > {
console.log("react", data);
});
}
start() {
this.hooks.arch.call("musion"); }}let l = new Lesson();
// Register both events
l.tap();
// Start the hook
l.start();
/** * The printed values are: * node musion * node musion * node musion * react musion */
Copy the code
Implementation of SyncLoopHook:
// The hook is synchronized
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 : "Continue learning";
});
hook.tap("node".function(data) {
console.log("node", data);
});
hook.tap("webpack".function(data) {
console.log("webpack", data);
});
hook.call("musion");
/** * The printed value is: * react musion * react musion * react musion * node musion * webpack musion */
Copy the code
AsyncParallelHook usage and implementation
AsyncParallelHook: AsyncParallelHook: AsyncParallelHook
let { AsyncParallelHook } = require("tapable");
// Asynchronous hooks can be either serial or parallel
// Serial: the second is executed only after the first is executed asynchronously
// parallelism: The callback method needs to wait for all concurrent asynchronous events to execute
// Register method: tap register tapAsync register
class Lesson {
constructor() {
this.hooks = {
arch: new AsyncParallelHook(["name"])}; }// Register the listener function
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();
// Register both events
l.tap();
// Start the hook
l.start();
/** * The printed values are: * node musion * react musion * end */
Copy the code
AsyncParallelHook implementation:
class SyncParralleHook {
constructor() {
this.tasks = [];
}
tapAsync(name, task) {
this.tasks.push(task);
}
callAsync(. args) {
// Take out the final function
let finalCallBack = args.pop();
let index = 0;
/ / similar 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");
});
/** * The printed value is: * react musion * react musion * react musion * node musion * webpack musion */
Copy the code
AsyncSeriesHook usage and implementation
AsyncSeriesHook is an asynchronous serial execution relationship, used as follows:
// AsyncSeriesHook AsyncSeriesHook
let { AsyncSeriesHook } = require("tapable");
class Lesson {
constructor() {
this.hooks = {
arch: new AsyncSeriesHook(["name"])}; }// Register the listener function
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();
// Register both events
l.tap();
// Start the hook
l.start();
/** * The printed values are: * node musion * react musion * end */
Copy the code
AsyncSeriesHook implementation:
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();
}
}
Copy the code
AsyncSeriesWaterfallHook usage and implementation
AsyncSeriesWaterfallHook AsyncSeriesWaterfallHook AsyncSeriesWaterfallHook AsyncSeriesWaterfallHook AsyncSeriesWaterfallHook AsyncSeriesWaterfallHook AsyncSeriesWaterfallHook AsyncSeriesWaterfallHook 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();
// Execute the first one
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");
});
/** * the printed value is: * node musion * end */
Copy the code
Implementation of 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();
// Execute the first one
if (index === 0) { task(... args, next); }else{ task(data, next); } index++; }; next(); }}Copy the code
Refer to the article
The second Tapable in the Webpack series
tapable
You can pay attention to my public account “Muchen classmates”, goose factory code farmers, usually record some trivial bits, technology, life, perception, grow together.