Plugin Hooks
本章节介绍 Rsbuild 插件可用的 plugin hooks。
总览
Common Hooks
Dev Hooks
仅在执行 rsbuild dev
命令或 rsbuild.startDevServer()
方法时调用。
Build Hooks
仅在执行 rsbuild build
命令或 rsbuild.build()
方法时调用。
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);
});
},
});
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!');
});
},
});