Daniel: Brother egg, what are we talking about today?
Mr. Egg: Today, let's talk about the key to the implementation of the plugin system in webpack - Tapable
Daniel: Tapable?
Mr. Egg: Yes, let's talk about a different way today, just talk about your day
Daniel: My day?
Mr. Egg: First of all, everyone's day has several stages: morning, noon, afternoon, evening. The description in Tapable way is as follows:
const { SyncHook } = require("tapable");
class Man {
constructor() {
this.hooks = {
morningHook: new SyncHook(),
noonHook: new SyncHook(),
afternoonHook: new SyncHook(),
nightHook: new SyncHook(),
};
}
startNewDay() {
this.hooks.morningHook.call();
this.hooks.noonHook.call();
this.hooks.afternoonHook.call();
this.hooks.nightHook.call();
}
}
Daniel: What is SyncHook?
Mr. Egg: Don't worry, you will understand later. First you are alone.
Daniel: Or else? Will it still be a beast? (`へ´)
Mr. Egg: (lll¬ω¬) Misunderstanding, look at the code
const daniel = new Man();
daniel.startNewDay();
Daniel: Oh, I see. So what am I going to do all day?
Mr. Egg: The first is the morning. In the morning, you do three things: get up, brush your teeth, and eat breakfast
Daniel: That's it? I thought there was a surprise
Mr. Egg: I'm not talking about jokes ╮(╯▽╰)╭, I only talk about code, come
const daniel = new Man();
// Morning
getUpAction(daniel);
brushTeethAction(daniel);
eatBreakfastAction(daniel);
daniel.startNewDay();
function getUpAction(manInst) {
manInst.hooks.morningHook.tap("getUp", () => console.log("Get up"));
}
function brushTeethAction(manInst) {
manInst.hooks.morningHook.tap("brushTeeth", () => console.log("Brush Teeth"));
}
function eatBreakfastAction(manInst) {
manInst.hooks.morningHook.tap("eatBreakfast", () =>
console.log("Eat breakfast")
);
}
Output result:
Get up
Brush Teeth
Eat breakfast
Daniel: I seem to see something, Man just defines life cycle hooks, but what each stage does is flexibly extended by adding behaviors (PS: The behaviors here can be understood as plug-ins, just to match this script only)
Mr. Egg: Yes that's right. The behaviors such as getting up and brushing teeth here are independent of each other, and they are all executed in synchronous order, so we only use the ordinary synchronous Hook, that is, SyncHook.
class Man {
constructor() {
this.hooks = {
morningHook: new SyncHook(),
...
};
}
startNewDay() {
this.hooks.morningHook.call();
...
}
}
Daniel: this.hooks.morningHook.call()
here is to inform the morning that this cycle phase has started, and then the previous actions have been registered in advance through manInst.hooks.morningHook.tap
to do something in this cycle, so each action is busy at this time, right?
Mr. Egg: Yes. Didn't you ask about SyncHook earlier? Because the behavior is synchronous and asynchronous, the Hook at the beginning of Sync is executed synchronously, and the one at the beginning of Async is executed asynchronously
Daniel: So that's the case, so many behaviors are hung in that cycle stage, do you have to wait for all behaviors to end before entering the next cycle stage?
Mr. Egg: Yes that's right. Multiple behaviors can be hung on a cycle stage. Generally, they are executed first (SyncXXX and AsyncSeriesXXX), and the other is concurrent execution. Of course, only asynchronous behaviors can be concurrent. Next, let's continue to learn about Tapable's various Hooks and other information through your day.
Daniel: Okay, we're done talking in the morning, what are we going to do at noon?
Mr. Egg: No, it's still morning. Let's tweak what we do in the morning a little bit, get up, make breakfast, eat breakfast
Daniel: Uh, it's still just as normal.
Mr. Egg: You screwed up making breakfast
Daniel: Ah, so unlucky? Then am I going to starve X﹏X
Mr. Egg: If you can't finish making breakfast, it means you need to interrupt breakfast. At this time, you need SyncBailHook
const { SyncBailHook } = require("tapable");
class Man {
constructor() {
this.hooks = {
morningHook: new SyncBailHook(),
...
};
}
...
}
const daniel = new Man();
// Morning
getUpAction(daniel);
makeBreakfastAction(daniel);
eatBreakfastAction(daniel);
daniel.startNewDay();
function getUpAction(manInst) {
manInst.hooks.morningHook.tap("getUp", () => console.log("Get up"));
}
function makeBreakfastAction(manInst) {
manInst.hooks.morningHook.tap("makeBreakfast", () => {
console.log("Make breakfast, but failed");
return false;
});
}
function eatBreakfastAction(manInst) {
manInst.hooks.morningHook.tap("eatBreakfast", () =>
console.log("Eat breakfast")
);
}
output result
Get up
Make breakfast, but failed
Daniel: Well, it's fine if you can't eat, you'll have to starve until noon
Mr. Egg: Not eating breakfast is bad for your health. I'll change the plot. You made a successful breakfast with milk, eggs and bread. But we need to give the results of making breakfast to eating breakfast, so that there is something to eat for breakfast, then we can use SyncWaterfallHook
const { SyncWaterfallHook } = require("tapable");
class Man {
constructor() {
this.hooks = {
morningHook: new SyncWaterfallHook(["breakfast"]),
...
};
}
...
}
function makeBreakfastAction(manInst) {
manInst.hooks.morningHook.tap("makeBreakfast", () => {
console.log("Make breakfast");
return "milk, bread, eggs";
});
}
function eatBreakfastAction(manInst) {
manInst.hooks.morningHook.tap("eatBreakfast", (breakfast) =>
console.log("Eat breakfast: ", breakfast)
);
}
Output result:
Get up
Make breakfast
Eat breakfast: milk, bread, eggs
Daniel: Thanks Eggman, it's been really nice to me. Breakfast is over, is it noon?
Mr. Egg: Yes, it's noon and you're cooking again
Daniel: Ah, I'm a foodie. What do I cook?
Mr. Egg: You cook rice and soup at the same time.
Daniel: On the one hand... on the other hand..., that is to do two things at the same time.
Mr. Egg: Yes, what behavior can be done at the same time, of course, asynchronous behavior, then you can use AsyncParallelHook
const { AsyncParallelHook } = require("tapable");
class Man {
constructor() {
this.hooks = {
...
noonHook: new AsyncParallelHook(),
...
};
}
async startNewDay() {
...
await this.hooks.noonHook.promise();
...
}
}
const daniel = new Man();
// Morning
...
// Noon
soupAction(daniel);
cookRiceAction(daniel);
daniel.startNewDay();
...
function cookRiceAction(manInst) {
manInst.hooks.noonHook.tapPromise("cookRice", () => {
console.log("cookRice starting...");
return new Promise((resolve) => {
setTimeout(() => {
console.log("cookRice finishing...");
resolve();
}, 800);
});
});
}
function soupAction(manInst) {
manInst.hooks.noonHook.tapPromise("soup", () => {
console.log("soup starting...");
return new Promise((resolve) => {
setTimeout(() => {
console.log("soup finishing...");
resolve();
}, 1000);
});
});
}
The output is as follows:
soup starting...
cookRice starting...
cookRice finishing...
soup finishing...
Daniel: Well, noon looks smoother than morning
Mr. Egg: It's the afternoon, and in the afternoon you start to study the Pomodoro Technique for four hours
Daniel: Well, you know I'm so eager to learn, you know me so well
Mr. Egg: Because a Pomodoro keeps looping until 4 hours have passed, you can use SyncLoopHook
const { SyncLoopHook } = require("tapable");
class Man {
constructor() {
this.hooks = {
...
afternoonHook: new SyncLoopHook(),
...
};
}
async startNewDay() {
...
this.hooks.afternoonHook.call();
...
}
}
const daniel = new Man();
// Morning
...
// Noon
...
// Afternoon
studyAction(daniel);
restAction(daniel)
daniel.startNewDay();
...
let leftTime = 4 * 60;
function studyAction(manInst) {
manInst.hooks.afternoonHook.tap("study", () => {
console.log("study 25 minutes");
leftTime -= 25;
});
}
function restAction(manInst) {
manInst.hooks.afternoonHook.tap("study", () => {
console.log("rest 5 minutes");
leftTime -= 5;
if (leftTime <= 0) {
console.log("tomatoStudy: finish");
return;
}
return true;
});
}
Output result:
study 25 minutes
rest 5 minutes
study 25 minutes
rest 5 minutes
study 25 minutes
rest 5 minutes
study 25 minutes
rest 5 minutes
study 25 minutes
rest 5 minutes
study 25 minutes
rest 5 minutes
study 25 minutes
rest 5 minutes
study 25 minutes
rest 5 minutes
tomatoStudy: finish
Daniel: I'm dizzy, it's time to relax at night
Mr. Egg: Well, at night, you may play games or watch movies, it depends on whether your friends are looking for you to score
Daniel: Oh, it depends on the situation, not all actions are performed, right?
Mr. Egg: Yes, this requires the use of HookMap
const { SyncHook, HookMap } = require("tapable");
class Man {
constructor() {
this.hooks = {
...
nightHook: new HookMap(() => new SyncHook()),
};
}
async startNewDay() {
...
this.hooks.nightHook.for("no friend invitation").call();
}
}
const daniel = new Man();
// Morning
...
// Noon
...
// Afternoon
...
// Night
playGameAction(daniel);
watchMovieAction(daniel);
daniel.startNewDay();
...
function playGameAction(manInst) {
manInst.hooks.nightHook.for("friend invitation").tap("playGame", () => {
console.log("play game");
});
}
function watchMovieAction(manInst) {
manInst.hooks.nightHook.for("no friend invitation").tap("watchMovie", () => {
console.log("watch movie");
});
}
Output result:
watch movie
Daniel: That's the end of the day, it's time to say goodbye
Mr. Egg: It's not over yet. You have a good habit of keeping a diary, and you keep a diary every time you do something.
Daniel: Remember everything? Is this a running account?
Mr. Egg: About the same, what do you think is the best way to remember it?
Daniel: Intercept before everything
Mr. Egg: Very clever, Interception can be used here
...
const daniel = new Man();
writeDiary(daniel);
...
daniel.startNewDay();
...
function writeDiary(manInst) {
const interceptFn = (hookName) => {
return {
tap: (tapInfo) => {
console.log(`write diary:`, tapInfo)
}
};
};
Object.keys(manInst.hooks).forEach((hookName) => {
if (manInst.hooks[hookName] instanceof HookMap) {
manInst.hooks[hookName].intercept({
factory: (key, hook) => {
hook.intercept(interceptFn(hookName));
return hook
},
});
} else {
manInst.hooks[hookName].intercept(interceptFn(hookName));
}
});
}
Output result:
write diary: { type: 'sync', fn: [Function], name: 'getUp' }
write diary: { type: 'sync', fn: [Function], name: 'makeBreakfast' }
write diary: { type: 'sync', fn: [Function], name: 'eatBreakfast' }
write diary: { type: 'promise', fn: [Function], name: 'soup' }
write diary: { type: 'promise', fn: [Function], name: 'cookRice' }
write diary: { type: 'sync', fn: [Function], name: 'study' }
write diary: { type: 'sync', fn: [Function], name: 'study' }
write diary: { type: 'sync', fn: [Function], name: 'study' }
write diary: { type: 'sync', fn: [Function], name: 'study' }
write diary: { type: 'sync', fn: [Function], name: 'study' }
write diary: { type: 'sync', fn: [Function], name: 'study' }
write diary: { type: 'sync', fn: [Function], name: 'study' }
write diary: { type: 'sync', fn: [Function], name: 'study' }
write diary: { type: 'sync', fn: [Function], name: 'watchMovie' }
Daniel: I've finished writing the diary, so there's nothing else to do
Mr. Egg: Finally, let's talk about context. Because each behavior may be provided by different developers, the behaviors are independent, but sometimes you want to share some data, for example, you need to share your personal information here, and then look at the last piece of code, then you can disperse, and then hold on for a while
...
const daniel = new Man();
writeDiary(daniel);
...
daniel.startNewDay();
function getUpAction(manInst) {
manInst.hooks.morningHook.tap(
{
name: "getUp",
context: true,
},
(context) => {
console.log("Get up", context);
}
);
}
...
function writeDiary(manInst) {
const interceptFn = (hookName) => {
return {
context: true,
tap: (context, tapInfo) => {
context = context || {};
context.userInfo = {
name: "daniel",
};
}
};
};
Object.keys(manInst.hooks).forEach((hookName) => {
if (manInst.hooks[hookName] instanceof HookMap) {
manInst.hooks[hookName].intercept({
factory: (key, hook) => {
console.log(`[${hookName}][${key}]`);
hook.intercept(interceptFn(hookName));
return hook;
},
});
} else {
manInst.hooks[hookName].intercept(interceptFn(hookName));
}
});
}
Output result:
Get up { userInfo: { name: 'daniel' } }
Daniel: I'm so sleepy, I can barely open my eyes
Mr. Egg: Okay, your day is over, goodbye
Daniel: Farewell
Keep reading, guys, how would you customize your day with Tapable?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。