Plugin Hooks
本章节介绍 Rsbuild 插件可用的 plugin hooks。
总览
Common Hooks
Dev Hooks
仅在执行 rsbuild dev
命令或 rsbuild.startDevServer()
方法时调用。
仅在 Rsbuild restart 时,或是执行 rsbuild.startDevServer()
的 close()
方法时调用。
Build Hooks
仅在执行 rsbuild build
命令或 rsbuild.build()
方法时调用。
仅在 Rsbuild restart 时,或是执行 rsbuild.build()
的 close()
方法时调用。
Preview Hooks
仅在执行 rsbuild preview
命令或 rsbuild.preview()
方法时调用。
Hooks 顺序
Dev Hooks
执行 rsbuild dev
命令或 rsbuild.startDevServer()
方法时,Rsbuild 会依次执行以下 hooks:
当 rebuild 时,以下 hooks 会再次触发:
Build Hooks
执行 rsbuild build
命令或 rsbuild.build()
方法时,Rsbuild 会依次执行以下 hooks:
当 rebuild 时,以下 hooks 会再次触发:
Preview Hooks
执行 rsbuild preview
命令或 rsbuild.preview()
方法时,Rsbuild 会依次执行以下 hooks:
Global Hooks vs Environment Hooks
在 Rsbuild 中,有一些插件 hooks 是全局 hooks,这些 hook 的执行往往和 Rsbuild 自身的启动流程或全局逻辑相关,在所有 environment 下共享。如:
modifyRsbuildConfig
用来修改 Rsbuild 的基础配置,基础配置最终会和 environment 配置合并;
onBeforeStartDevServer
、onAfterStartDevServer
和 Rsbuild dev server 启动流程相关,所有 environments 共享 Rsbuild 的 dev server、middlewares、Web Socket。
与之对应的,有一些插件 hooks 是和当前 environment 相关的 hook,这些 hook 执行时会带有特定的 environment 上下文,并根据 environment 的不同而触发多次。
Global Hooks
Environment Hooks
回调函数顺序
默认行为
如果多个插件注册了相同的 hook,那么 hook 的回调函数会按照注册时的顺序执行。
在以下例子中,控制台会依次输出 '1'
和 '2'
:
const plugin1 = () => ({
setup: (api) => {
api.modifyRsbuildConfig(() => console.log('1'));
},
});
const plugin2 = () => ({
setup: (api) => {
api.modifyRsbuildConfig(() => console.log('2'));
},
});
rsbuild.addPlugins([plugin1, plugin2]);
order 字段
在注册 hook 时,可以通过 order
字段来声明 hook 的顺序。
type HookDescriptor<T extends (...args: any[]) => any> = {
handler: T;
order: 'pre' | 'post' | 'default';
};
在以下例子中,控制台会依次输出 '2'
和 '1'
,因为 plugin2 在调用 modifyRsbuildConfig 时设置了 order 为 pre
。
const plugin1 = () => ({
setup: (api) => {
api.modifyRsbuildConfig(() => console.log('1'));
},
});
const plugin2 = () => ({
setup: (api) => {
api.modifyRsbuildConfig({
handler: () => console.log('2'),
order: 'pre',
});
},
});
rsbuild.addPlugins([plugin1, plugin2]);
Common Hooks
modifyRsbuildConfig
修改传递给 Rsbuild 的配置项,你可以直接修改传入的 config 对象,也可以返回一个新的对象来替换传入的对象。
type ModifyRsbuildConfigUtils = {
mergeRsbuildConfig: typeof mergeRsbuildConfig;
};
function ModifyRsbuildConfig(
callback: (
config: RsbuildConfig,
utils: ModifyRsbuildConfigUtils,
) => MaybePromise<RsbuildConfig | void>,
): void;
const myPlugin = () => ({
setup: (api) => {
api.modifyRsbuildConfig((config) => {
config.html ||= {};
config.html.title = 'My Default Title';
});
},
});
- 示例: 通过
mergeRsbuildConfig
合并配置多个对象,并返回合并后的对象。
import type { RsbuildConfig } from '@rsbuild/core';
const myPlugin = () => ({
setup: (api) => {
api.modifyRsbuildConfig((userConfig, { mergeRsbuildConfig }) => {
const extraConfig: RsbuildConfig = {
source: {
// ...
},
output: {
// ...
},
};
// extraConfig 会覆盖 userConfig 里的字段,
// 如果你不希望覆盖 userConfig,可以调整为 `mergeRsbuildConfig(extraConfig, userConfig)`
return mergeRsbuildConfig(userConfig, extraConfig);
});
},
});
TIP
modifyRsbuildConfig
不能用于注册额外的 Rsbuild 插件。这是因为在执行 modifyRsbuildConfig
时,Rsbuild 已经初始化了所有插件,并开始执行 hooks 的回调函数。详情可参考 插件注册时机。
modifyEnvironmentConfig
修改特定 environment 的 Rsbuild 配置。
在回调函数中,入参里的 config 对象已经合并了公共的 Rsbuild 配置,你可以直接修改这个 config 对象,也可以返回一个新的对象来替换它。
type ArrayAtLeastOne<A, B> = [A, ...Array<A | B>] | [...Array<A | B>, A];
type ModifyEnvironmentConfigUtils = {
/** 当前 environment 名称 */
name: string;
mergeEnvironmentConfig: (
...configs: ArrayAtLeastOne<MergedEnvironmentConfig, EnvironmentConfig>
) => EnvironmentConfig;
};
function ModifyEnvironmentConfig(
callback: (
config: EnvironmentConfig,
utils: ModifyEnvironmentConfigUtils,
) => MaybePromise<EnvironmentConfig | void>,
): void;
- 示例: 为指定 environment 的 Rsbuild config 设置一个默认值:
const myPlugin = () => ({
setup: (api) => {
api.modifyEnvironmentConfig((config, { name }) => {
if (name !== 'web') {
return config;
}
config.html.title = 'My Default Title';
});
},
});
- 示例: 通过
mergeEnvironmentConfig
合并配置多个对象,并返回合并后的对象。
import type { EnvironmentConfig } from '@rsbuild/core';
const myPlugin = () => ({
setup: (api) => {
api.modifyEnvironmentConfig((userConfig, { mergeEnvironmentConfig }) => {
const extraConfig: EnvironmentConfig = {
source: {
// ...
},
output: {
// ...
},
};
// extraConfig 会覆盖 userConfig 里的字段
// 如果你不希望覆盖 userConfig,可以调整为 `mergeEnvironmentConfig(extraConfig, userConfig)`
return mergeEnvironmentConfig(userConfig, extraConfig);
});
},
});
modifyRspackConfig
修改 Rspack 配置,你可以直接修改传入的 config 对象,也可以返回一个新的对象来替换传入的对象。
TIP
modifyRspackConfig
的执行时机早于 tools.rspack。因此,无法在 modifyRspackConfig
中获取到 tools.rspack
所做的修改。
type ModifyRspackConfigUtils = {
environment: EnvironmentContext;
env: string;
isDev: boolean;
isProd: boolean;
target: RsbuildTarget;
isServer: boolean;
isWebWorker: boolean;
rspack: Rspack;
};
function ModifyRspackConfig(
callback: (
config: Rspack.Configuration,
utils: ModifyRspackConfigUtils,
) => Promise<RspackConfig | void> | Rspack.Configuration | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.modifyRspackConfig((config, utils) => {
if (utils.env === 'development') {
config.devtool = 'eval-cheap-source-map';
}
});
},
});
modifyBundlerChain
rspack-chain 是一个用于配置 Rspack 的工具库。它提供了链式 API,使得配置 Rspack 变得更加灵活。通过使用 rspack-chain
,你可以更方便地修改和扩展 Rspack 配置,而不需要直接操作复杂的配置对象。
modifyBundlerChain
用于调用 rspack-chain 来修改 Rspack 的配置,它的用法与 tools.bundlerChain 相同。
type ModifyBundlerChainUtils = {
environment: EnvironmentContext;
env: string;
isDev: boolean;
isProd: boolean;
target: RsbuildTarget;
isServer: boolean;
isWebWorker: boolean;
CHAIN_ID: ChainIdentifier;
HtmlPlugin: typeof import('html-rspack-plugin');
bundler: {
BannerPlugin: rspack.BannerPlugin;
DefinePlugin: rspack.DefinePlugin;
IgnorePlugin: rspack.IgnorePlugin;
ProvidePlugin: rspack.ProvidePlugin;
HotModuleReplacementPlugin: rspack.HotModuleReplacementPlugin;
};
};
function ModifyBundlerChain(
callback: (
chain: RspackChain,
utils: ModifyBundlerChainUtils,
) => Promise<void> | void,
): void;
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
const myPlugin = () => ({
setup: (api) => {
api.modifyBundlerChain((chain, utils) => {
if (utils.env === 'development') {
chain.devtool('eval');
}
chain.plugin('bundle-analyze').use(BundleAnalyzerPlugin);
});
},
});
modifyHTMLTags
修改注入到 HTML 中的标签。
type HtmlBasicTag = {
// 标签名
tag: string;
// 标签的属性
attrs?: Record<string, string | boolean | null | undefined>;
// 标签的 innerHTML
children?: string;
};
type HTMLTags = {
// 插入到 <head> 的标签组
headTags: HtmlBasicTag[];
// 插入到 <body> 的标签组
bodyTags: HtmlBasicTag[];
};
type Context = {
/**
* Rspack 的 Compiler 对象
*/
compiler: Rspack.Compiler;
/**
* Rspack 的 Compilation 对象
*/
compilation: Rspack.Compilation;
/**
* 静态资源的 URL 前缀
* @example 'https://example.com/'
*/
assetPrefix: string;
/**
* HTML 文件的名称,相对于 dist 目录
* @example 'index.html'
*/
filename: string;
/**
* 当前构建的 environment 上下文
*/
environment: EnvironmentContext;
};
function ModifyHTMLTags(
callback: (tags: HTMLTags, context: Context) => MaybePromise<HTMLTags>,
): void;
const myPlugin = () => ({
setup: (api) => {
api.modifyHTMLTags(({ headTags, bodyTags }) => {
headTags.push({
tag: 'script',
attrs: { src: 'https://example.com/foo.js' },
});
bodyTags.push({
tag: 'script',
children: 'console.log("hello world!");',
});
return { headTags, bodyTags };
});
},
});
onBeforeCreateCompiler
onBeforeCreateCompiler
是在创建底层 Compiler 实例前触发的回调函数,当你执行 rsbuild.startDevServer
、rsbuild.build
或 rsbuild.createCompiler
时,都会调用此钩子。
你可以通过 bundlerConfigs
参数获取到 Rspack 配置数组,数组中可能包含一份或多份 Rspack 配置,这取决于是否配置了多个 environments。
function OnBeforeCreateCompiler(
callback: (params: {
bundlerConfigs: Rspack.Configuration[];
environments: Record<string, EnvironmentContext>;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onBeforeCreateCompiler(({ bundlerConfigs }) => {
console.log('the bundler config is ', bundlerConfigs);
});
},
});
onAfterCreateCompiler
onAfterCreateCompiler
是在创建 Compiler 实例后、执行构建前触发的回调函数,当你执行 rsbuild.startDevServer
、rsbuild.build
或 rsbuild.createCompiler
时,都会调用此钩子。
你可以通过 compiler
参数获取到 Compiler 实例对象:
function OnAfterCreateCompiler(callback: (params: {
compiler: Compiler | MultiCompiler;
environments: Record<string, EnvironmentContext>;
}) => Promise<void> | void;): void;
const myPlugin = () => ({
setup: (api) => {
api.onAfterCreateCompiler(({ compiler }) => {
console.log('the compiler is ', compiler);
});
},
});
onBeforeEnvironmentCompile
onBeforeEnvironmentCompile
是在执行单个 environment 的构建前触发的回调函数。
你可以通过 bundlerConfig
参数获取到当前 environment 对应的 Rspack 配置。
另外,你可以通过 isWatch
判断是否是 dev 或者 build watch 模式,并在 watch 模式下通过 isFirstCompile
来判断是否为首次构建。
function OnBeforeEnvironmentCompile(
callback: (params: {
isWatch: boolean;
isFirstCompile: boolean;
bundlerConfig?: Rspack.Configuration;
environment: EnvironmentContext;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onBeforeEnvironmentCompile(({ bundlerConfig, environment }) => {
console.log(
`the bundler config for the ${environment.name} is `,
bundlerConfig,
);
});
},
});
onAfterEnvironmentCompile
onAfterEnvironmentCompile
是在执行单个 environment 的构建后触发的回调函数,你可以通过 stats 参数获取到构建结果信息。
另外,你可以通过 isWatch
判断是否是 dev 或者 build watch 模式,并通过 isFirstCompile
来判断是否为首次构建。
function OnAfterEnvironmentCompile(
callback: (params: {
isFirstCompile: boolean;
isWatch: boolean;
stats?: Stats;
environment: EnvironmentContext;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onAfterEnvironmentCompile(({ isFirstCompile, stats }) => {
console.log(stats?.toJson(), isFirstCompile);
});
},
});
Build Hooks
onBeforeBuild
onBeforeBuild
是在执行生产模式构建前触发的回调函数。
你可以通过 bundlerConfigs
参数获取到 Rspack 配置数组,数组中可能包含一份或多份 Rspack 配置,这取决于是否配置了多个 environments。
另外,你可以通过 isWatch
判断是否是 watch 模式,并在 watch 模式下通过 isFirstCompile
来判断是否为首次构建。
function OnBeforeBuild(
callback: (params: {
isWatch: boolean;
isFirstCompile: boolean;
bundlerConfigs?: Rspack.Configuration[];
environments: Record<string, EnvironmentContext>;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onBeforeBuild(({ bundlerConfigs }) => {
console.log('the bundler config is ', bundlerConfigs);
});
},
});
onAfterBuild
onAfterBuild
是在执行生产模式构建后触发的回调函数,你可以通过 stats 参数获取到构建结果信息。
另外,你可以通过 isWatch
判断是否是 watch 模式,并在 watch 模式下通过 isFirstCompile
来判断是否为首次构建。
function OnAfterBuild(
callback: (params: {
isFirstCompile: boolean;
isWatch: boolean;
stats?: Stats | MultiStats;
environments: Record<string, EnvironmentContext>;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onAfterBuild(({ isFirstCompile, stats }) => {
console.log(stats?.toJson(), isFirstCompile);
});
},
});
onCloseBuild
在关闭构建时调用,可用于在构建关闭时执行清理操作。
Rsbuild CLI 会在执行 rsbuild build 完成后自动调用此钩子,使用 JavaScript API 的用户需要手动调用 rsbuild.close() 方法来触发此钩子。
function onCloseBuild(callback: () => Promise<void> | void): void;
const myPlugin = () => ({
setup: (api) => {
api.onCloseBuild(() => {
console.log('close build!');
});
},
});
Dev Hooks
onBeforeStartDevServer
在启动开发服务器前调用。
function OnBeforeStartDevServer(
callback: (params: {
environments: Record<string, EnvironmentContext>;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onBeforeStartDevServer(() => {
console.log('before start!');
});
},
});
onAfterStartDevServer
在启动开发服务器后调用。你可以通过 port
参数获得开发服务器监听的端口号,通过 routes
获得页面路由信息。
type Routes = Array<{
entryName: string;
pathname: string;
}>;
function OnAfterStartDevServer(
callback: (params: {
port: number;
routes: Routes;
environments: Record<string, EnvironmentContext>;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onAfterStartDevServer(({ port, routes }) => {
console.log('this port is: ', port);
console.log('this routes is: ', routes);
});
},
});
onAfterEnvironmentCompile
onAfterEnvironmentCompile
是在执行单个 environment 的构建后触发的回调函数,你可以通过 stats 参数获取到构建结果信息。
另外,你可以通过 isWatch
判断是否是 dev 或者 build watch 模式,并通过 isFirstCompile
来判断是否为首次构建。
function OnAfterEnvironmentCompile(
callback: (params: {
isFirstCompile: boolean;
isWatch: boolean;
stats?: Stats;
environment: EnvironmentContext;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onAfterEnvironmentCompile(({ isFirstCompile }) => {
if (isFirstCompile) {
console.log('first compile!');
} else {
console.log('re-compile!');
}
});
},
});
onDevCompileDone
在每次开发模式构建结束后调用,你可以通过 isFirstCompile
来判断是否为首次构建。
function OnDevCompileDone(
callback: (params: {
isFirstCompile: boolean;
stats: Stats | MultiStats;
environments: Record<string, EnvironmentContext>;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onDevCompileDone(({ isFirstCompile }) => {
if (isFirstCompile) {
console.log('first compile!');
} else {
console.log('re-compile!');
}
});
},
});
onCloseDevServer
关闭开发服务器时调用,可用于在开发服务器关闭时执行清理操作。
function onCloseDevServer(callback: () => Promise<void> | void): void;
rsbuild.onCloseDevServer(async () => {
console.log('close dev server!');
});
Preview Hooks
onBeforeStartProdServer
在启动生产预览服务器前调用。
function OnBeforeStartProdServer(callback: () => Promise<void> | void): void;
const myPlugin = () => ({
setup: (api) => {
api.onBeforeStartProdServer(() => {
console.log('before start!');
});
},
});
onAfterStartProdServer
在启动生产预览服务器后调用,你可以通过 port
参数获得生产服务器监听的端口号,通过 routes
获得页面路由信息。
type Routes = Array<{
entryName: string;
pathname: string;
}>;
function OnAfterStartProdServer(
callback: (params: {
port: number;
routes: Routes;
environments: Record<string, EnvironmentContext>;
}) => Promise<void> | void,
): void;
const myPlugin = () => ({
setup: (api) => {
api.onAfterStartProdServer(({ port, routes }) => {
console.log('this port is: ', port);
console.log('this routes is: ', routes);
});
},
});
Other Hooks
onExit
在进程即将退出时调用,这个钩子只能执行同步代码。
function OnExit(callback: () => void): void;
const myPlugin = () => ({
setup: (api) => {
api.onExit(() => {
console.log('exit!');
});
},
});