插件化架构
插件化架构(Plug-in Architecture),也被称为微内核架构(Microkernel Architecture),是一种面向功能进行拆分的可扩展性架构,在如今的许多前端主流框架中都能看到它的身影。今天我们以 umi 框架为主,来看看插件化架构的实现思路,同时对比一下不同框架中插件化实现思路的异同。
各个主流框架插件化异同
二话不说先上结论。
触发方式 | 插件 API | 插件功能 | |
---|---|---|---|
umi | 基于 tapable 的发布订阅模式 | 10 种核心方法,50 种扩展方法,9 个核心属性 | 在路由、生成文件、构建打包、HTML 操作、命令等方面提供能力 |
babel | 基于 visitor 的访问者模式 | 基于@babel/types | 对于 AST 的操作等 |
rollup | 基于 hook 的回调模式 | 构建钩子、输出钩子、监听钩子 | 定制构建和打包阶段的能力 |
webpack | 基于 tapable 的发布订阅模式 | 主要为 compolier 和 compilation 提供一系列的钩子 | loader 不能实现的都靠它 |
vue-cli | 基于 hook 的回调模式 | 生成阶段为 Generator API,运行阶段为 chainWebpack 等更改 webpack 配置为主的 api | 在生成项目、项目运行和 vue ui 阶段提供能力 |
一个完整的插件系统应该包括三个部分:
插件内核(plugiCore):用于管理插件;
插件接口(pluginApi):用于提供 api 给插件使用;
插件(plugin):功能模块,不同的插件实现不同的功能。
因此我们也从这三部分入手去分析 umi 的插件化。
umi 插件(plugin)
我们先从最简单的开始,认识一个umi 插件长什么样。我们以插件集preset(@umijs/preset-built-in)中的一个内置插件umiInfo(packages/preset-built-in/src/plugins/features/umiInfo.ts)为例,来认识一下 umi 插件。
import { IApi } from '@umijs/types';
export default (api: IApi) => {
// 调用扩展方法addHTMLHeadScripts在 HTML 头部添加脚本
api.addHTMLHeadScripts(() => [
{
content: `//! umi version: ${process.env.UMI_VERSION}`,
},
]);
// 调用扩展方法addEntryCode在入口文件最后添加代码
api.addEntryCode(
() => `
window.g_umi = {
version: '${process.env.UMI_VERSION}',
};
`,
);
};
可以看到 umi 插件导出了一个函数,函数内部为调用传参 api 上的两个方法属性,主要实现了两个功能,一个是在 html 文件头部添加脚本,另一个是在入口文件最后添加代码。其中,preset是一系列插件的合集。代码非常简单,就是 require 了一系列的plugin。插件集preset(packages/preset-built-in/src/index.ts)如下:
export default function () {
return {
plugins: [
// 注册方法插件
require.resolve('./plugins/registerMethods'),
// 路由插件
require.resolve('./plugins/routes'),
// 生成文件相关插件
require.resolve('./plugins/generateFiles/core/history'),
……
// 打包配置相关插件
require.resolve('./plugins/features/404'),
……
// html操作相关插件
require.resolve('./plugins/features/html/favicon'),
……
// 命令相关插件
require.resolve('./plugins/commands/build/build'),
……
}
这些plugin主要包括一个注册方法插件(packages/preset-built-in/src/plugins/registerMethods.ts),一个路由插件(packages/preset-built-in/src/plugins/routes.ts),一些生成文件相关插件(packages/preset-built-in/src/plugins/generateFiles/*),一些打包配置相关插件(packages/preset-built-in/src/plugins/features/*),一些html 操作相关插件(packages/preset-built-in/src/plugins/features/html/*)以及一些命令相关插件(packages/preset-built-in/src/plugins/commands/*)。
在注册方法插件registerMethods(packages/preset-built-in/src/plugins/registerMethods.ts)中,umi集中注册了几十个方法,这些方法就是umi文档中插件 api 的扩展方法。
export default function (api: IApi) {
// 集中注册扩展方法
[
'onGenerateFiles',
'onBuildComplete',
'onExit',
……
].forEach((name) => {
api.registerMethod({ name });
});
// 单独注册writeTmpFile方法,并传参fn,方便其他扩展方法使用
api.registerMeth