Rspack:webpack 兼容 API 之下的 Rust 应用打包器
核验日期:2026-07-09。示例版本来自
examples/build-tools/package.json:@rspack/core2.1.2、@rspack/cli2.1.2。本文以 Rspack 2.1 为主线,同时说明从 Rspack 1.7 到 Rspack 2 的迁移边界。
需要掌握的问题
| 主题 | 读者必须回答的问题 | 原理与边界 | 高频错误 | 代码或观察证据 | 来源 |
|---|---|---|---|---|---|
| 定位与兼容目标 | Rspack 为什么不是“另一个 webpack 配置模板”? | Rspack 暴露接近 webpack 的 JavaScript 配置、Loader 和 Plugin 生态接口,但核心编译、图构建、优化和产物生成由 Rust 实现 | 以为所有 webpack 插件无条件兼容,或把 Rspack 当 Vite/Rsbuild 一样的开箱框架工具 | 共享 rspack.config.mjs 沿用 entry、rules、plugins、optimization、stats 等 webpack 风格配置 | Introduction |
| JS API 到 Rust core | rspack(config)、CLI、Compiler、Compilation 和 Rust core 如何协作? | JavaScript 层负责配置解析、Loader/Plugin 调用和宿主接口;Rust core 执行解析、依赖收集、module graph、chunk graph、代码生成和优化 | 在 Plugin 里依赖 webpack 私有对象结构,或在 Loader 里做大量同步 I/O 抵消 Rust core 性能 | JS/Rust 边界 Mermaid、Plugin/Loader 表 | Plugins |
| resolver、Loader、Plugin 兼容层 | webpack 迁移时哪些扩展点最容易出问题? | resolver 负责把请求变成模块 id;Loader 仍按模块源码转换运行;Plugin 通过 webpack 风格 hooks 介入编译阶段,但兼容列表只覆盖常见插件 | 只看“webpack 兼容”四个字,不检查具体插件状态和替代插件 | builtin:swc-loader、HtmlRspackPlugin、插件兼容表 | Plugin compatibility、Loader |
| 图与产物 | Rspack 的 module graph、chunk graph 和 runtime 分别解决什么? | module graph 记录模块和依赖;chunk graph 把 entry、dynamic import、splitChunks 等映射为可加载文件;runtime 负责浏览器端 chunk 加载和模块执行 | HTML 直接加载 async chunk,或只看文件数量不看 chunk 来源 | dist/rspack/stats.json、artifact test 中的 detail.ts 异步 chunk 断言 | Code splitting |
| 内置 SWC、CSS 与 minifier | Rspack 为什么会比“webpack + 一堆 loader/minifier”少一些固定成本? | builtin:swc-loader、内置 CSS 模块类型、SWC/Lightning CSS minimizer 把常见重活放进内置实现,减少 JavaScript 插件链路 | 继续套旧 babel-loader、css-loader、mini-css-extract-plugin,导致迁移后性能收益很小 | 示例 TS 规则使用 builtin:swc-loader,CSS 规则使用 type: 'css/auto' | Builtin swc-loader、CSS |
| incremental、persistent cache、HMR 与 lazy compilation | 开发阶段为什么快,哪些优化不影响生产构建? | incremental compilation 复用图和编译状态;persistent cache 持久化可复用结果;HMR 只重建受影响模块;lazy compilation 只在 dev 中按访问编译 entry 和 dynamic import | 把 lazy compilation 当生产优化;缓存键漏掉 Rspack/Loader/环境版本;HMR 成功但 production build 失败 | cache.type: 'persistent'、lazyCompilation 配置和官方 2.1 缓存清理说明 | Lazy compilation、Rspack 2.1 release |
| stats、profile 与 Rsdoctor | 怎样定位构建慢、分包异常或插件慢? | stats.json 提供 assets/chunks/modules 结构;profile 和 Rsdoctor 用于分析耗时、依赖图、bundle 体积和异常插件 | 只用肉眼看 dist/,无法证明 async chunk、CSS、Source Map 和 tree shaking 正确 | rspack build --json dist/rspack/stats.json、Rsdoctor 入口 | Bundle analysis、Use Rsdoctor |
| Module Federation 构建边界 | Rspack 的 Module Federation 适合解决什么,不解决什么? | Rspack 提供 MF v1.5 内置支持,也可用 MF v2.0 enhanced 插件;它解决运行时远程模块共享,不替代团队契约、版本治理和部署回滚 | remote URL、shared singleton、uniqueName、类型提示和缓存策略没有治理,导致线上运行时失败 | rspack.container.ModuleFederationPlugin 配置片段和边界表 | Module Federation |
| Rspack 与 Rsbuild 分工 | 什么时候直接用 Rspack,什么时候用 Rsbuild? | Rspack 是底层 bundler;Rsbuild 是基于 Rspack 的上层构建工具,提供更开箱的 dev/build 体验和框架约定 | 应用项目手写大量底层 Rspack 配置,或库/框架工具误把 Rsbuild 当唯一入口 | Rspack 官方在与 Vite 对比处推荐应用体验使用 Rsbuild | Ecosystem |
| 迁移与重大变化 | Rspack 1.7 → 2.0 → 2.1 哪些变化会影响工程? | Rspack 2 提升 Node 要求、ESM 发布、dev server 依赖、parser 默认值、内置 loader 行为;2.1 改进性能、缓存清理、CSS 和输出优化 | Node 18 项目直接升级、旧 .swcrc 仍期待被 builtin:swc-loader 读取、exports 缺失从 warning 变 error | 迁移表、2.1 benchmark 条件 | Upgrading from v1 to v2、Rspack 2.1 release |
| native binding 与供应链 | 为什么 Rspack 的“快”也带来新的安全和运维边界? | Rspack 安装 native binding;Loader/Plugin 仍以 Node 权限执行;Module Federation 还会加载远程运行时代码 | CI 平台缺 native 包、锁文件未固定、远程容器无 SRI/版本约束、缓存被跨分支错误复用 | 锁版本、校验 binary 平台、限制插件权限和远程入口 | Migrate from webpack |
内容边界说明:
| 分类 | 本文落点 |
|---|---|
| 编程语言与类型系统 | 只解释 TypeScript/JSX 通过 SWC 转换、ESM/CJS 依赖如何进入构建图;不把 Rspack 当类型检查器 |
| Browser APIs 与持久化能力 | 覆盖 <script>、CSS、dynamic import、HMR WebSocket、Source Map 和浏览器 chunk runtime;不讨论 IndexedDB、Storage、权限模型 |
| UI 框架与元框架 | 说明 React Refresh、Module Federation、Rsbuild/框架工具边界;组件调度、渲染和水合由框架专栏负责 |
| 构建工具与运行时 | 完整覆盖输入、解析、转换、依赖图、chunk graph、优化、缓存、插件、产物和故障定位 |
| 后端、数据与 Agent 技术 | 只覆盖构建插件以开发者权限运行、CI native binding、远程模块供应链和产物可观测性;请求生命周期和数据库一致性不适用 |
Rspack 的主问题:保留 webpack 迁移面,把重活放进 Rust
Rspack 的设计取舍很明确:不是抛弃 webpack 生态重新发明配置模型,而是在尽量保留 webpack-compatible 使用面的同时,用 Rust core 重写编译和优化热路径。对大型存量应用来说,真正的成本不是“写一个新配置”,而是成千上万个 Loader、Plugin、alias、splitChunks、Module Federation、dev server 和 CI 脚本如何迁移。
和相邻工具的边界:
| 工具 | 主要问题 | Rspack 与它的关系 |
|---|---|---|
| webpack | 最成熟的应用打包器和插件生态 | Rspack 对齐大量 webpack 配置、Loader 和 Plugin,但不是 100% 私有实现兼容 |
| esbuild | 极快的转换与打包 API | Rspack 更接近 webpack 应用模型,覆盖 HMR、splitChunks、Module Federation 等复杂场景 |
| Rollup/Rolldown | 以 ESM 图和库/生产打包为主 | Rspack 更偏应用 runtime、webpack 迁移和微前端边界 |
| Vite | 开发服务器和应用构建体验 | 直接用 Rspack 要自己组织 dev/build 体验;应用层可评估 Rsbuild |
| Rsbuild | Rspack 上层构建工具 | Rsbuild 提供开箱约定;Rspack 提供底层可编程打包能力 |
严重问题完整流程:
| 症状 | 根因 | 观察命令 | 修正 |
|---|---|---|---|
| “换成 Rspack 还是慢” | 仍然跑大量 JavaScript Loader/Plugin,或插件在 hooks 中做同步 I/O | 开 profile/Rsdoctor,看耗时集中在 loader/plugin 还是 Rust 编译阶段 | 用内置 SWC/CSS/minifier 替代可替代插件;把慢插件延后或隔离 |
webpack 插件运行时报 compilation.xxx is not a function | 插件依赖 webpack 私有实现或未兼容的 hook 细节 | 对照 Rspack Plugin compatibility 表和插件 issue | 换 Rspack 内置插件/替代插件,或保留 webpack 直到插件迁移 |
| dev 正常、build 后白屏 | HMR/lazy compilation 路径没有覆盖生产 chunk、base、runtime 和 CSS 提取 | 运行生产 build,并检查 stats.json、HTML、Network | 不用 dev 成功替代生产验收 |
从 JavaScript API 到 Rust core:兼容层不是免费午餐
Rspack 的配置表面和 webpack 非常接近。共享示例 examples/build-tools/configs/rspack.config.mjs 保留了典型 webpack 应用构建配置;下面是省略路径和 stats 细节后的节选:
import { HtmlRspackPlugin } from '@rspack/core';
export default {
mode: 'production',
target: ['web', 'es2022'],
entry: './src/main.ts',
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: { parser: { syntax: 'typescript' } },
},
},
},
{
test: /\.css$/,
type: 'css/auto',
},
],
},
plugins: [
new HtmlRspackPlugin({
templateContent: '<div id="app"></div>',
}),
],
output: {
filename: 'assets/[name].[contenthash:8].js',
chunkFilename: 'assets/[name].[contenthash:8].js',
cssFilename: 'assets/[name].[contenthash:8].css',
publicPath: './',
clean: true,
},
};这段配置经过三层:
| 层 | 输入 | 处理 | 输出/边界 |
|---|---|---|---|
| JavaScript 配置层 | rspack.config.mjs、CLI 参数、环境变量 | 解析配置、实例化插件、准备 Compiler | 配置本身可以执行任意 Node 代码,属于供应链边界 |
| webpack 兼容层 | resolver 请求、Loader 请求、Plugin hooks | 调用 JavaScript Loader/Plugin,把结果交回核心流程 | Loader/Plugin 越多,JS/native 边界越重 |
| Rust core | 模块源码、依赖请求、优化配置 | parse、构建图、chunk、minify、emit | 高性能来自这里,但无法让不兼容插件自动正确 |
一个容易误判的点是:webpack 兼容 不是“所有 webpack 内部对象都是同一个对象”。适合迁移的配置通常满足:
- 主要依赖公开配置项、普通 Loader、常见 Plugin。
- Loader 返回标准
{ code, map }或 callback 结果,不依赖 webpack 内部未文档化对象。 - Plugin 使用稳定 hooks,不直接读写复杂内部结构。
- 构建验收覆盖 HTML、CSS、async chunk、Source Map 和 stats,而不是只看命令退出码。
resolver、Loader、Plugin:迁移先按扩展点拆风险
Rspack 的迁移风险主要集中在三类扩展点:
| 扩展点 | 负责什么 | 迁移风险 | 验证方式 |
|---|---|---|---|
| resolver | 把 import './x'、alias、condition exports、tsconfig paths 解析成模块 id | alias、exports condition、PnP、大小写、软链接策略和 webpack 有差异 | 打印解析结果,检查重复模块和 external |
| Loader | 对单个模块源码做转换,例如 TS、CSS、SVG、MDX | JavaScript Loader 仍串行执行;读取上下文或缓存声明不完整会慢或错 | 缩小到单个文件,检查输出 code/map 和 cacheable 语义 |
| Plugin | 通过 hooks 介入 Compiler/Compilation/asset 输出 | 私有 hook、内部对象、子编译器、HTML 插件生态最容易遇到差异 | 查兼容表,跑真实 build,检查 stats 和产物 |
内置能力能替代的部分优先替代:
export default {
module: {
rules: [
{
test: /\.(?:js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'builtin:swc-loader',
options: {
detectSyntax: 'auto',
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'automatic' } },
},
},
},
},
{
test: /\.css$/,
type: 'css/auto',
},
],
},
};官方插件兼容表要按“具体插件 + 具体用法”读,而不是按“Compatible”一刀切:
| 状态 | 工程含义 |
|---|---|
| Included | Rspack 已内置类似能力,通常应改用 Rspack 插件或配置,例如 CssExtractRspackPlugin |
| Compatible | 常见用法可工作,但仍要用项目配置验收;个别选项可能不支持 |
| Alternative | 不建议继续用原 webpack 插件,应换 Rspack/Rsbuild 生态替代 |
| Partially compatible | 只支持基础用法,复杂选项要单独验证 |
| Incompatible | 不应直接迁移;需要替代方案、保留 webpack 或重写插件 |
严重问题完整流程:
| 症状 | 根因 | 观察命令 | 修正 |
|---|---|---|---|
| 同一个包被打进两份 | resolver condition、alias、symlink 或 tsconfig paths 结果不一致 | 在 stats.json 搜索包名和 resolved path | 收敛 alias/exports condition,启用对应 resolver 配置 |
| Source Map 指到生成代码 | Loader 返回 code 但丢了上游 map | 解析最终 .map.sources 是否包含 .ts/.css | 使用能生成 map 的转换器,保持 source map 链 |
| HTML 插件产物缺资源 | 插件 hook 兼容不完整,或使用 webpack-only option | 对照插件兼容表,检查 assets 和 HTML | 换 HtmlRspackPlugin 或 Rspack 替代插件 |
module graph、chunk graph 与 runtime:应用打包的核心状态
Rspack 不是逐文件转换器。它从 entry 出发建立完整依赖图,再把图切成浏览器可加载的 chunk。
三个图相关概念要分开:
| 概念 | 解决的问题 | 可观察证据 |
|---|---|---|
| module graph | 模块之间谁依赖谁、依赖类型是什么、导出是否被使用 | stats.json.modules、缺失导出错误、Tree Shaking 结果 |
| chunk graph | 哪些模块放进哪些初始/异步 chunk | stats.json.chunks、HTML 是否只引用 initial chunk |
| runtime | 浏览器如何注册模块、加载 async chunk、处理 publicPath | 输出 JS 中的 runtime 代码、Network 请求和 chunk 文件名 |
共享源码里 main.ts 包含一个动态导入:
button.addEventListener('click', async () => {
const { renderDetail } = await import('./detail');
renderDetail(panel);
});预期结果是:
dist/rspack/index.html存在,并只直接引用初始 JS/CSS。dist/rspack/stats.json存在,可追踪 entry、assets、chunks、modules。detail.ts进入异步 chunk,而不是被 HTML 直接<script>加载。- JS/CSS source map 存在,且 sources 能追溯到
main.ts、detail.ts、styles.css。 - 未使用标记
SHOULD_NOT_REACH_BUNDLE不应出现在最终 JS 中,但副作用标记preserved必须保留。
运行命令:
pnpm --filter @roadmap/build-tools-slice build:rspack
node --test --test-name-pattern rspack examples/build-tools/test/artifacts.test.mjs如果 stats.json 很大,不要直接人工翻完整文件。先用脚本提取关键信息:
import stats from './dist/rspack/stats.json' with { type: 'json' };
for (const chunk of stats.chunks ?? []) {
console.log(chunk.id, chunk.names, chunk.files);
}built-in SWC、CSS 与 minifier:性能收益来自减少 JS 插件链路
Rspack 的性能收益不是“Rust”一个词自动带来的。更实际的收益来自把常见热路径变成内置实现:
| 能力 | webpack 常见组合 | Rspack 推荐起点 | 边界 |
|---|---|---|---|
| TypeScript/JSX 转换 | babel-loader、ts-loader、swc-loader | builtin:swc-loader | 不做类型检查;类型检查仍应由 tsc 或 ts-checker-rspack-plugin 负责 |
| CSS 模块与提取 | css-loader、style-loader、mini-css-extract-plugin | 内置 CSS 类型和 CssExtractRspackPlugin | 复杂 PostCSS、CSS-in-JS 仍需对应插件 |
| JS minify | terser-webpack-plugin | SwcJsMinimizerRspackPlugin 或 production 默认策略 | Terser 特有压缩语义需回归 |
| CSS minify | css-minimizer-webpack-plugin | LightningCssMinimizerRspackPlugin | Lightning CSS 的语法兼容和目标浏览器要验收 |
| HTML 输出 | html-webpack-plugin | HtmlRspackPlugin | HTML 插件生态选项并非全部等价 |
类型检查边界要明确:builtin:swc-loader 会把 TypeScript 转成 JavaScript,但不会证明类型正确。
{
"scripts": {
"build": "rspack build --config rspack.config.mjs",
"typecheck": "tsc --noEmit"
}
}严重问题完整流程:
| 症状 | 根因 | 观察命令 | 修正 |
|---|---|---|---|
| TypeScript 类型错但 build 通过 | SWC 只转译,不类型检查 | pnpm typecheck | CI 中单独跑类型检查;需要集成可使用 checker 插件 |
| CSS 顺序或作用域变了 | 从旧 loader 组合迁移到内置 CSS 后语义未回归 | 构建后用浏览器检查样式和 CSS Modules class | 对 CSS Modules、PostCSS、sideEffects 做专项迁移测试 |
| React 编译链变慢 | 仍通过 Babel React Compiler 或多层 loader 处理 | profile 查看 loader 耗时 | 在 Rspack 2.1 中评估 builtin:swc-loader 的 Rust React Compiler 路径 |
incremental、persistent cache、HMR 与 lazy compilation:只在正确生命周期里才有效
开发体验通常由四个机制共同决定:
| 机制 | 解决的问题 | 不解决的问题 |
|---|---|---|
| incremental compilation | 文件变更后复用图和中间状态,只重建受影响部分 | 首次冷构建、生产产物正确性 |
| persistent cache | 跨进程/跨启动复用可缓存结果 | 配置、版本、环境变化后的自动正确性;仍要设计缓存键 |
| HMR | 浏览器不刷新或少刷新地替换模块 | 状态迁移、生产 chunk/base/minify |
| lazy compilation | dev server 启动时不立即编译未访问 entry/dynamic import | production build;未访问路径第一次打开的等待时间 |
Rspack 2.1 对 persistent cache 增加了自动清理:旧缓存版本可按 cache.maxAge 和 cache.maxVersions 控制保留,默认方向是避免长期分支切换后缓存目录无限增长。典型配置:
export default {
cache: {
type: 'persistent',
maxAge: 7 * 24 * 60 * 60,
maxVersions: 3,
},
};lazy compilation 只对开发构建有效。它代理未执行的 entry 和 dynamic import,运行时访问时再向 dev server 发请求触发编译:
export default {
entry: {
home: './src/home.ts',
admin: './src/admin.ts',
},
lazyCompilation: {
entries: true,
imports: true,
test(module) {
const name = module.nameForCondition();
return name ? !/always-eager/.test(name) : true;
},
},
};错误完整流程:
| 症状 | 根因 | 观察命令 | 修正 |
|---|---|---|---|
| CI 偶发复用旧产物 | persistent cache key 没包含 Rspack/Node/Loader/环境差异 | 打印 cache directory 和 Rspack 版本,清缓存重跑对照 | CI 用干净缓存或把关键版本写入 cache.version |
| 首次访问某个页面卡顿 | lazy compilation 把该 entry 延后到首次访问 | 开 dev server 后首次打开对应路由看编译日志 | 对高频入口禁用 lazy,或只 lazy dynamic imports |
| HMR 后状态错乱 | 模块副作用没有 dispose,或 Module Federation remote/runtime 边界复杂 | 浏览器控制台和 HMR 日志 | 给模块或框架插件定义明确 HMR 边界;不能热更时 full reload |
stats、bundle analysis、profile 与 Rsdoctor:不要用肉眼猜图
本仓库脚本会写出 stats.json:
pnpm --filter @roadmap/build-tools-slice build:rspack对应 package script 是:
{
"build:rspack": "rspack build --config configs/rspack.config.mjs --json dist/rspack/stats.json"
}stats.json 的主要用途不是存档,而是回答可验证问题:
| 问题 | 从 stats/分析工具看什么 |
|---|---|
| 为什么有两个 vendor chunk? | chunk 的 modules、issuer、splitChunks 分组 |
| 某个页面为什么加载了后台模块? | module issuer chain 和 dynamic/static dependency 类型 |
| CSS 为什么重复? | assets、chunk files、CSS module 所属 chunk |
| 构建慢在哪? | profile/Rsdoctor 中 loader、plugin、optimize、emit 的耗时 |
| Tree Shaking 是否生效? | 模块 used exports、最终 JS 搜索和 minify 后结果 |
Rspack 官方同时提供 Bundle analysis、Build performance profile 和 Use Rsdoctor 文档。工程里建议按顺序排查:
- 先用
stats.json证明产物结构是否符合预期。 - 再用 profile 定位时间花在 core、Loader、Plugin、minify 还是 emit。
- 复杂项目使用 Rsdoctor 看依赖图、体积、耗时和异常模块。
- 最后才调整 splitChunks、cache、Loader 或插件。
严重问题完整流程:
| 症状 | 根因 | 观察命令 | 修正 |
|---|---|---|---|
| async chunk 被 initial HTML 加载 | HTML 插件或运行时配置误把异步资源注入 | 检查 index.html 和 stats.json.chunks[].initial | 只注入 initial chunk;动态路径交给 runtime |
| 构建时间集中在 minify | 压缩目标或插件过重 | profile/Rsdoctor | 换内置 SWC/Lightning CSS minifier,或按环境关闭昂贵 sourcemap |
| 体积突然变大 | external/alias/sideEffects/splitChunks 变化 | stats diff | 建立 stats 对照结果,PR 中比较关键 chunk |
Module Federation:构建能接线,运行时契约仍要治理
Rspack 提供一等 Module Federation 支持。官方文档把它定位为 JavaScript 应用拆分和多应用共享代码的架构模式,常见于微前端。
最小内置 v1.5 入口:
import { rspack } from '@rspack/core';
export default {
output: {
uniqueName: 'shell',
publicPath: 'auto',
},
plugins: [
new rspack.container.ModuleFederationPlugin({
name: 'shell',
remotes: {
catalog: 'catalog@https://cdn.example.com/catalog/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^19.0.0' },
'react-dom': { singleton: true, requiredVersion: '^19.0.0' },
},
}),
],
};构建边界和组织边界必须分开:
| 层 | Rspack 能做 | 团队仍要做 |
|---|---|---|
| 构建 | 生成 container/remote runtime、shared 依赖描述、chunk 加载代码 | 定义 remote 发布、回滚、灰度和版本组合 |
| 运行时 | 按 remoteEntry 加载模块,处理 shared 解析 | 保证 URL 可用、SRI/CSP/缓存策略正确 |
| 类型 | MF v2.0 enhanced 可提供动态 TS 类型提示等能力 | 把类型兼容纳入 CI 和契约测试 |
| HMR | uniqueName 等配置影响 HMR/runtime 隔离 | 多 remote 热更新和状态迁移仍要框架层处理 |
严重问题完整流程:
| 症状 | 根因 | 观察命令 | 修正 |
|---|---|---|---|
| 本地正常,线上 remote 加载 404 | remoteEntry 路径、publicPath、CDN 缓存不一致 | Network 查 remoteEntry 和子 chunk | 给每个 remote 建发布契约和静态健康检查 |
| React hooks/context 异常 | shared singleton 或版本不一致 | 在 runtime 打印 shared scope | 对 framework runtime 使用 singleton 和严格版本策略 |
| HMR 串应用或 chunk 名冲突 | output.uniqueName 未隔离 | 查看 runtime 全局 chunk 名 | 每个容器显式配置唯一 uniqueName |
Rspack 与 Rsbuild:底层 bundler 和上层构建工具不要混用心智模型
直接使用 Rspack 的价值是控制底层构建细节:Loader、Plugin、Module Federation、chunk、stats、cache、runtime。代价是应用体验要自己组织。
Rsbuild 是 Rspack 上层构建工具。它适合需要更开箱的应用工程体验,例如 dev server、框架插件、约定式配置和与 Rspack 生态集成。官方在 Rspack 与 Vite 对比里也明确提到,如果寻找类似 Vite 的 out-of-the-box 开发体验,应评估 Rsbuild。
| 场景 | 直接 Rspack | Rsbuild |
|---|---|---|
| 从 webpack 大型应用迁移 | 适合,配置映射更直接 | 可用,但需要接受上层约定 |
| 需要自定义 bundler runtime、stats、MF 细节 | 适合 | 可能要下探到底层配置 |
| 普通 React/Vue 应用开发体验 | 能做,但配置成本更高 | 更适合 |
| 框架作者或构建平台 | 适合做底座 | 适合做面向业务的封装 |
判断标准:如果团队讨论的是 module.rules、optimization.splitChunks、ModuleFederationPlugin、stats 和自定义插件,直接 Rspack 更自然;如果讨论的是“如何快速启动一个应用并保持默认最佳实践”,Rsbuild 更自然。
共享示例:用同一份输入验证 Rspack 应用构建职责
本仓库示例使用同一份源码对比八种工具。Rspack 负责完整浏览器产物,而不是单文件转换。
运行:
pnpm --filter @roadmap/build-tools-slice build:rspack预期结果:
| 文件 | 用途 | 必须观察的事实 |
|---|---|---|
examples/build-tools/dist/rspack/index.html | 浏览器入口 | 只引用相对路径的初始 JS/CSS |
examples/build-tools/dist/rspack/stats.json | 构建元数据 | 能看到 assets、chunks、modules |
examples/build-tools/dist/rspack/assets/*.js | 初始和异步 JS chunk | 包含 Babel、preserved,不包含 SHOULD_NOT_REACH_BUNDLE |
examples/build-tools/dist/rspack/assets/*.css | CSS 产物 | 样式从 styles.css 进入 bundle |
*.map | 调试映射 | JS map 包含 main.ts/detail.ts,CSS map 包含 styles.css |
自动验收:
node --test --test-name-pattern rspack examples/build-tools/test/artifacts.test.mjs这组测试的意义是把“Rspack 能构建”拆成可观察契约:HTML、stats、Source Map、异步 chunk、CSS、Tree Shaking 和副作用保留都要同时成立。
迁移与重大变化:1.7 → 2.0 → 2.1 不只是升级版本号
迁移表
| 版本线 | 重大变化 | 工程动作 |
|---|---|---|
| Rspack 1.7.x | 作为 Rspack 1 稳定线,适合与旧 webpack 迁移经验对齐 | 记录当前 webpack 插件、Loader、stats、MF、dev server 行为作为对照结果 |
| Rspack 2.0 | Node 最低要求提升到 20.19+ 或 22.12+;核心包转为 pure ESM;@rspack/cli 不再默认依赖 dev server;parser/loader/output 多项默认值变化 | 升级 Node;同步升级 @rspack/core、CLI、dev-server、react-refresh 插件;逐项检查 breaking changes |
| Rspack 2.1 | 改进 build/HMR 性能、TypeScript 7 支持、循环依赖检查、import.meta.glob、内置 CSS、createRequire 解析、persistent cache 清理、输出优化 | 打开回归测试;评估内置 CSS/SWC/缓存清理;对 tree shaking 和 chunk 做 stats diff |
Rspack 2.1 release 给出的性能数字来自 rspack-react-10k-benchmark,条件必须保留,不应写成所有项目的绝对承诺:
| 版本 | Production build no cache | Production build with cache | HMR |
|---|---|---|---|
| Rspack 1.7.11 | 3.12 s | 2.09 s | 129 ms |
| Rspack 2.0.0 | 2.66 s | 1.36 s | 113 ms |
| Rspack 2.1.0 | 2.22 s | 1.20 s | 107 ms |
正确读法:
- 这是官方在特定 React 10k benchmark 上的结果。
- 它能证明 2.1 相比 2.0 在该条件下有生产构建和 HMR 改进。
- 你的项目还要单独测,因为 Loader/Plugin、CSS、Source Map、CI 磁盘、缓存命中都会改变结果。
重大变化和严重问题
| 症状 | 可能原因 | 定位 | 修正 |
|---|---|---|---|
| 升级后 Node 报 ESM 或版本错误 | Rspack 2 提升 Node 要求并转 pure ESM | node -v、检查 require('@rspack/core') 调用 | 升级 Node;改用 ESM import 或 Node 支持的 require(esm) |
rspack dev 找不到 dev server | CLI 不再默认依赖 @rspack/dev-server | 看 package.json devDependencies | 显式安装兼容版本 @rspack/dev-server |
| 原本 warning 的缺失导出变成 error | exportsPresence 默认行为变化 | 查看报错模块和 parser 配置 | 修正错误导入;必要时临时设回兼容配置 |
builtin:swc-loader 不读 .swcrc | Rspack 2 改为在 loader options 中配置 | 搜索 .swcrc 和规则配置 | 把 SWC 配置迁到 rspack.config.mjs |
| bundle analyzer 命令消失 | CLI 内置 analyzer 移除 | 运行旧 --analyze | 改用 Rsdoctor 或兼容分析工具 |
native binding、插件执行和远程代码:安全边界必须显式写进流程
Rspack 的安全边界有三层:
| 边界 | 风险 | 控制措施 |
|---|---|---|
| native binding | 平台包缺失、锁文件污染、CI 架构不一致、供应链投毒 | 锁定 @rspack/core 版本和 lockfile;CI 使用固定 Node/pnpm;检查安装平台 |
| Loader/Plugin | 构建时以开发机/CI 的 Node 权限执行任意代码 | 审查新增构建依赖;限制 CI secrets;避免在不可信源码上运行任意插件 |
| Module Federation remote | 浏览器运行时加载远程代码,风险转移到发布和 CDN | 固定 remote 版本、配置 CSP/SRI、健康检查、回滚和共享依赖契约 |
缓存也是安全和正确性边界。persistent cache 不应跨不可信分支共享;CI 缓存 key 至少包含:
node-version
pnpm-lock-hash
rspack-version
loader-plugin-version
rspack-config-hash
target-env严重问题完整流程:
| 症状 | 根因 | 观察命令 | 修正 |
|---|---|---|---|
| CI 安装 Rspack 后 native module 加载失败 | 平台 optional dependency 缺失或包管理器裁剪 | pnpm list @rspack/core、检查 lockfile 中平台包 | 固定 pnpm,保留 optional deps,必要时换支持平台 |
| PR 泄露 CI secret | 构建插件在 install/build 阶段读取环境变量并上传 | 审查依赖脚本和网络访问 | PR 构建不给生产 secret;构建依赖走白名单 |
| remote 被缓存成旧版本 | CDN cache key 和 remoteEntry 发布策略不匹配 | Network 查看 remoteEntry 响应头和 chunk URL | remoteEntry 短缓存,chunk 长 hash 缓存,发布记录可回滚 |