Статья впервые опубликована на:GitHub.com/US TB-Вуд, умри, о ты…
Tapable
Несколько слов о вебпакеEverything is a plugin, Webpack — это, по сути, механизм потока событий, его рабочий процесс заключается в последовательном соединении различных плагинов, а ядром всего этого является Tapable. Tapable чем-то похож на библиотеку событий nodejs, и основным принципом также является модель зависимости и публикации-подписки. Компилятор, ответственный за компиляцию ядра веб-пакета, и компилятор, отвечающий за создание пакетов, являются экземплярами Tapable. Ниже представлено использование и принцип Tapable. Исходный адрес кода для следующего примера:GitHub.com/US TB-Вуд, умри, о ты…
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");
Обзор Tapable Hook
Tapable предоставляет множество типов хуков, которые делятся на две категории: синхронные и асинхронные (в асинхронных различают асинхронные параллельные и асинхронные последовательные), а по условиям завершения выполнения события выводятся типы Bail/Waterfall/Loop.
На диаграмме ниже показано, что делает каждый тип:
- BasicHook:Выполняйте каждый, не заботясь о возвращаемом значении функции, есть SyncHook, AsyncParallelHook, AsyncSeriesHook.
- BailHook:Хуки выполняются последовательно, и возвращается первый результат !== undefined, и выполнение не будет продолжено. Есть: SyncBailHook, AsyncSeriseBailHook, AsyncParallelBailHook.
- WaterfallHook:Как и в случае с уменьшением, если результат предыдущей функции-ловушки result !== undefined, результат будет использоваться в качестве первого параметра последней функции-ловушки. Поскольку он выполняется последовательно, этот хук предоставляется только в классах Sync и AsyncSeries: SyncWaterfallHook, AsyncSeriesWaterfallHook.
- LoopHook:Хук выполняется в цикле до тех пор, пока все результаты функции не приведут к результату === undefined. Точно так же из-за зависимости от серийности есть только SyncLoopHook и AsyncSeriseLoopHook (PS: я пока не видел конкретного варианта использования)
Использование и реализация SyncHook
Синхронизация - это отношение выполнения синхронного последовательного канала, использование выглядит следующим образом:
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 — это отношение синхронного последовательного выполнения. Пока возвращаемое значение одной функции в функции прослушивания не равно нулю, вся оставшаяся логика будет пропущена. Использование выглядит следующим образом:
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, функция прослушивателя будет выполняться повторно. Если она возвращает неопределенное значение, это означает выход из цикла. Использование выглядит следующим образом:
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 — это отношение асинхронного последовательного выполнения. Второй параметр обратного вызова (ошибка, данные) в предыдущей функции прослушивателя может использоваться в качестве параметра следующей функции прослушивателя. Использование выглядит следующим образом:
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();
}
}
Справочная статья
Можете обратить внимание на мой паблик-аккаунт «Muchen Classmate», фермера на гусиной фабрике, который обычно записывает какие-то банальные мелочи, технологии, жизнь, инсайты и срастается.