Umi 插件概述
什么是插件
Umi 的核心就在于它的插件机制。那什么是插件机制呢?如果你有 webpack 基础,那你应该知道 webpack 整个架构很大程度上是基于事件的,每个 webpack 插件基本上都是一组在编译阶段挂钩不同事件的监听器。webpack 在底层使用了一个叫做 tapable 的库来封装“发布-订阅”的实现。
tapable
提供了不同的“钩子”类(SyncBailHook、AsyncParallelHook 等)来“钩起”具有一些额外丰富功能(例如拦截或跨侦听器集成)的事件。umi 的插件机制也是基于 tapable
实现的。
如果你听不懂上面的表达,也没有关系,以下我用一些伪代码来让你明白什么是插件,插件是如何运行的。当然,这并不是真正的插件运行机制,我只是为了便于理解做了大量的简化。
js
class Umi {
constructor(plugins) {
this.plugins = plugins;
}
init() {
console.log("umi core init");
this.plugins.map((i) => {
i?.init?.();
});
}
generateFile() {
console.log("umi core generateFile");
this.plugins.map((i) => {
i?.generateFile?.();
});
}
build() {
console.log("umi core build");
this.plugins.map((i) => {
i?.build?.();
});
}
run() {
this.init();
this.generateFile();
this.build();
}
}
const plugin1 = {
init: () => {
console.log("[plugin1] init");
},
build: () => {
console.log("[plugin1] build");
},
};
const plugin2 = {
generateFile: () => {
console.log("[plugin2] generateFile");
},
build: () => {
console.log("[plugin2] build");
},
};
const plugins = [plugin1, plugin2];
const umiCore = new Umi(plugins);
umiCore.run();
run
里面的内容就是定义好的生命周期,当执行 run
的时候,我们会按顺序执行 init
,generateFile
,build
,并且在内部生命周期函数中,使用 plugins.map
来触发每个插件中定义的钩子函数。
上面的代码 运行结果
js
umi core init
[plugin1] init
umi core generateFile
[plugin2] generateFile
umi core build
[plugin1] build
[plugin2] build
如果你理解了上面的代码,那加入一个自定义的 EventEmitter
来模拟 tapable
库的“发布-订阅”逻辑。 我们就得到了下面的代码。
js
class EventEmitter {
constructor() {
this.subscriptions = new Set();
}
emit = (val) => {
for (const subscription of this.subscriptions) {
subscription(val);
}
};
useSubscription = (callback) => {
function subscription(val) {
if (callback) {
callback(val);
}
}
this.subscriptions.add(subscription);
};
}
const initEmitter = new EventEmitter();
const buildEmitter = new EventEmitter();
class Umi {
constructor(plugins) {
plugins.map((i) => i());
}
init() {
console.log("umi core init");
initEmitter.emit();
}
build() {
console.log("umi core build");
buildEmitter.emit();
}
run() {
this.init();
this.build();
}
}
const plugin1 = () => {
initEmitter.useSubscription(() => {
console.log("[plugin1] init");
});
buildEmitter.useSubscription(() => {
console.log("[plugin1] build");
});
};
const plugin2 = () => {
buildEmitter.useSubscription(() => {
console.log("[plugin2] build");
});
};
const plugins = [plugin1, plugin2];
const umiCore = new Umi(plugins);
umiCore.run();
同样的复制上面的代码,在控制台中运行查看结果。
ts
umi core init
[plugin1] init
umi core build
[plugin1] build
[plugin2] build
在这次的生命周期函数中,使用 emit
来“发布”事件。在每个插件中使用 useSubscription
来“订阅”事件。当执行“发布”时,所有的“订阅”都将被响应。
以上为了说明仅仅展示了两个生命周期,在 umi 生态中,有一套更加完整丰富的生命周期,并且全部通过 umi 的 api
暴露给插件。
查看项目所有的插件
ts
npx umi plugin list