服务端5月28日 04:05
如何从 Webpack 迁移到 Rspack?Rspack 是字节跳动开源的基于 Rust 的高性能构建工具,从设计之初就以 Webpack 兼容性为核心目标。Rspack 1.0 已于 2024 年 10 月正式发布,到 2026 年已成为生产就绪的 Webpack 替代方案,在大型项目中可实现 5-10 倍的构建速度提升。以下是完整的迁移路径和关键要点。
## 核心答案
**Rspack 与 Webpack 的配置兼容性约 95%**,大多数项目可在 1-2 天内完成迁移。迁移的核心步骤:替换依赖 → 重命名配置文件 → 更新构建脚本 → 修复不兼容项 → 验证构建产物一致性。Yelp 等公司的实际迁移案例显示,迁移后构建时间减少约 50%,HMR 速度从 3-5 秒缩短到 500 毫秒以内。
## 迁移步骤
### 1. 替换依赖
卸载 Webpack 相关包,安装 Rspack 对应包:
```bash
# 卸载 Webpack 依赖
npm uninstall webpack webpack-cli webpack-dev-server
# 安装 Rspack 依赖
npm install @rspack/core @rspack/cli -D
# 如果使用 dev-server
npm install @rspack/dev-server -D
```
使用 pnpm 或 yarn 时同理。注意 Rspack 要求 Node.js >= 16。
### 2. 迁移配置文件
将 `webpack.config.js` 复制为 `rspack.config.js`,大部分配置可以直接复用:
```js
// rspack.config.js — 从 webpack.config.js 复制后调整
const rspack = require('@rspack/core');
module.exports = {
entry: './src/index.js', // 入口配置完全兼容
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
// 大部分 loader 规则可以直接复用
],
},
plugins: [
new rspack.HtmlWebpackPlugin({ template: './public/index.html' }),
],
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
};
```
需要修改的地方主要是导入语句:将 `require('webpack')` 替换为 `require('@rspack/core')`。
### 3. 更新构建脚本
```json
{
"scripts": {
"dev": "rspack serve",
"build": "rspack build"
}
}
```
### 4. 修复不兼容项
这是迁移中耗时最多的环节,常见的需要调整的配置:
- **loader 替换**:移除 `ts-loader` 和 `babel-loader`,Rspack 内置 `swc-loader`,原生支持 TypeScript 和 JSX 编译
- **插件替换**:`TerserWebpackPlugin` 用 Rspack 内置的 `SwcJsMinimizerRspackPlugin` 替代;`MiniCssExtractPlugin` 用 `rspack.CssExtractRspackPlugin` 替代
- **自定义插件**:如果项目中使用了访问 `compilation.entrypoints` 等内部 API 的自定义插件,需要对照 Rspack 的 API 进行调整
```js
// Rspack 内置能力替代示例
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'builtin:swc-loader', // 内置 swc,替代 ts-loader
options: {
jsc: {
parser: { syntax: 'typescript', tsx: true },
},
},
},
type: 'javascript/auto',
},
],
},
};
```
### 5. 验证构建产物
迁移完成后,务必对比 Webpack 和 Rspack 的构建产物,确保功能一致性:
- 对比产出文件数量和体积
- 运行全量测试用例
- 检查运行时行为是否一致(特别是动态 import、代码分割、环境变量注入等)
- 使用 `rspack build --profile` 分析构建产物
## 兼容性详情
| 配置项 | 兼容程度 | 说明 |
|--------|---------|------|
| Entry | 完全兼容 | 所有入口配置方式均支持 |
| Output | 大部分兼容 | 部分冷门选项有差异 |
| Module Rules | 大部分兼容 | loader 生态覆盖率 90%+ |
| Resolve | 完全兼容 | 别名、扩展名等均支持 |
| Plugins | 部分兼容 | 常用插件覆盖率 90-95%,自定义插件需检查 |
| Dev Server | 接近兼容 | `@rspack/dev-server` API 与 `webpack-dev-server` 高度一致 |
## 常见问题排查
### 某个 Webpack 插件不兼容怎么办?
首先查阅 [Rspack 官方兼容性列表](https://rspack.rs/guide/migration/webpack)。如果确实不支持,可以:1)寻找该插件在 Rspack 中的替代方案;2)通过 `compatLayer` 配置尝试兼容运行;3)暂时保留 Webpack 构建路径,采用渐进式迁移。
### 迁移后构建速度没有明显提升?
检查是否仍在使用 JavaScript 实现的 loader(如 `postcss-loader`、`sass-loader`),它们会成为性能瓶颈。优先替换为 Rspack 内置的 Rust 实现或社区提供的 SWC-based 方案。同时检查 `source-map` 配置,开发环境建议使用 `cheap-module-source-map`。
### 大型 monorepo 如何迁移?
推荐采用适配器模式(Adapter Pattern):创建统一的构建配置工厂函数,根据环境变量选择输出 Webpack 或 Rspack 配置。Yelp 在迁移其 monorepo 时采用了分阶段上线策略,先在新分支验证,再逐步放量。保留 Webpack 配置作为回滚方案,直到 Rspack 构建稳定运行至少一个迭代周期。
### TypeScript 项目需要额外配置吗?
Rspack 内置 SWC 支持 TypeScript 编译,可以移除 `ts-loader`、`fork-ts-checker-webpack-plugin` 等相关依赖。但注意 Rspack 不执行类型检查,需要单独运行 `tsc --noEmit` 或在 IDE 中处理类型检查。
## 性能对比参考
基于社区基准测试和实际迁移案例的数据:
| 指标 | Webpack | Rspack | 提升 |
|------|---------|--------|------|
| 冷启动时间 | 30s+ | ~1.4s | 约 20 倍 |
| 生产构建 | 30-60s | 3-4s | 约 10 倍 |
| HMR 增量编译 | 3-5s | <500ms | 约 8 倍 |
| 内存占用 | 较高 | 较低 | 30-50% |
以上数据来自中大型 React 项目,实际效果因项目规模和配置复杂度而异。
## 迁移策略选择
### 何时选择 Rspack?
- 现有项目使用 Webpack 且迁移到 Vite 成本过高(如重度依赖 Webpack 特有插件链)
- 大型 monorepo 或企业级应用需要显著的构建速度提升
- 团队熟悉 Webpack 生态,希望最低学习成本获得性能收益
### 何时考虑其他方案?
- 全新项目且不依赖 Webpack 生态 → 优先考虑 Vite
- Next.js 项目 → Turbopack 是官方推荐的加速方案
- Vue 生态项目 → Rsbuild(基于 Rspack 的上层方案)提供更开箱即用的体验
Rspack 的定位是 Webpack 项目的低风险高回报迁移路径,而非所有场景的最优解。选择工具时首先要明确项目的实际瓶颈和团队的迁移预算。标签
Rspack
Rspack 是一个现代化的前端构建工具,旨在替代传统的 Webpack,提供更高效、更快速的打包体验。它采用 Rust 语言开发,利用 Rust 的高性能和安全特性,实现了极致的构建速度和资源优化。Rspack 支持模块热更新(HMR)、代码分割、Tree Shaking 等前端开发中常用的功能,同时兼容 Webpack 的配置和插件生态,方便开发者无缝迁移和使用。相比传统的 JavaScript 实现,Rspack 在处理大型项目时表现出更低的内存占用和更快的构建时间,极大提升了开发效率和用户体验。此外,Rspack 还注重开发者体验,提供了友好的错误提示和丰富的调试信息,帮助开发者快速定位问题。它不仅适用于单页应用(SPA),也支持多页应用(MPA)和微前端架构,满足不同规模和复杂度项目的需求。随着前端技术的发展,Rspack 作为新一代构建工具,正逐步成为前端工程师优化构建流程、提升性能的有力选择。

前端5月27日 17:34
Rspack 的缓存机制是如何工作的?Rspack 的缓存机制是提升构建性能的核心手段。Rspack 目前支持内存缓存(Memory Cache)和持久化缓存(Persistent Cache)两种类型,配合快照策略、构建依赖追踪、可移植缓存等机制,能够在开发调试和生产构建中显著缩短耗时。
## 内存缓存
内存缓存是 Rspack 最基础的缓存形式,将模块编译结果和依赖图保存在内存中,使得增量构建和 HMR 能够快速响应。
在开发模式下,Rspack 默认启用内存缓存:
```javascript
module.exports = {
cache: true
}
```
也可以显式指定类型:
```javascript
module.exports = {
cache: {
type: 'memory'
}
}
```
内存缓存的特点是速度极快,但进程退出后缓存即丢失。对于日常开发中的热更新场景,内存缓存已经足够,这也是 Rspack 在 HMR 性能上表现优异的原因之一。
在生产模式下,`cache` 默认为 `false`,即不启用任何缓存。
## 持久化缓存
持久化缓存将构建结果写入磁盘,使得下次启动时可以直接复用上一次的编译产物,而无需重新执行模块解析和代码转换。这对于大型项目的冷启动场景尤为关键。
### 基本配置
```javascript
const path = require('path')
module.exports = {
cache: {
type: 'persistent'
}
}
```
启用后,Rspack 默认将缓存存储在 `node_modules/.cache/rspack` 目录下。你也可以自定义存储位置:
```javascript
module.exports = {
cache: {
type: 'persistent',
storage: {
type: 'filesystem',
directory: path.resolve(__dirname, '.cache/rspack')
}
}
}
```
### 构建依赖(buildDependencies)
`buildDependencies` 用于声明哪些文件的变更应当导致缓存失效。Rspack 会计算这些文件的哈希值,当哈希值发生变化时,持久化缓存自动失效。
```javascript
module.exports = {
cache: {
type: 'persistent',
buildDependencies: [__filename, path.join(__dirname, 'tsconfig.json')]
}
}
```
需要注意的是,与 webpack 不同,Rspack 默认不预设任何构建依赖项。如果你希望配置文件修改后缓存能正确失效,必须将配置文件路径加入 `buildDependencies`。
### 缓存版本(version)
`version` 字段用于隔离不同配置的缓存。不同 version 值的缓存互不干扰,Rspack 会为每个版本生成独立的缓存目录。
```javascript
module.exports = {
cache: {
type: 'persistent',
version: '1.0.0'
}
}
```
当项目配置发生重大变更(如升级 loader 版本、修改 babel 配置等)时,更新 version 可以避免旧缓存导致的构建错误。需要注意的是,不要在配置不同的构建之间共享相同的 version 和 storage.directory,否则可能命中错误的缓存。
### 缓存清理(maxGenerations)
Rspack 通过 `maxGenerations` 控制缓存的存活周期。默认值为 1,意味着如果某条缓存在下一次编译中没有被使用,就会被清理。增大该值可以让缓存存活更多轮次:
```javascript
module.exports = {
cache: {
type: 'persistent',
maxGenerations: 5
}
}
```
此外,Rspack 在启动时会自动清理超过 7 天未被访问的缓存目录,无需手动维护。
## 快照策略(snapshot)
快照策略决定了 Rspack 如何判断文件是否在上次构建后被修改,直接影响缓存验证的效率。
### managedPaths
`managedPaths` 用于指定由包管理器管理的目录(默认包含 `node_modules`)。对这些路径下的文件,Rspack 通过 `package.json` 中的 `version` 字段判断是否变更,而不是逐文件计算哈希,从而大幅加速缓存验证。
```javascript
module.exports = {
cache: {
type: 'persistent',
snapshot: {
managedPaths: [/node_modules/]
}
}
}
```
### immutablePaths
`immutablePaths` 用于指定内容不会变更的路径。一旦路径被标记为不可变,Rspack 在热重启时会跳过对这些文件的检查,直接认为缓存有效:
```javascript
module.exports = {
cache: {
type: 'persistent',
snapshot: {
immutablePaths: [path.join(__dirname, 'dist')]
}
}
}
```
### unmanagedPaths
`unmanagedPaths` 用于排除不应被 managedPaths 规则覆盖的路径。如果你的 `node_modules` 中有通过 git submodule 等方式管理的包,可以将其加入此列表,让 Rspack 对这些文件采用更精确的哈希验证。
## 可移植缓存(portable)
可移植缓存是 Rspack 为 CI/CD 场景设计的能力。启用后,缓存序列化时会将绝对路径转换为相对路径,使得缓存可以在不同机器和操作系统之间共享。
```javascript
module.exports = {
cache: {
type: 'persistent',
portable: true
}
}
```
典型的应用场景是在 CI 环境中:先将缓存构建并上传为 artifact,后续的构建任务直接下载复用,无需每次从零开始编译。Windows、Linux、macOS 之间可以共享同一份缓存,不需要为每个平台维护独立的缓存副本。
需要注意的是,可移植模式下,项目目录外的文件会被转换为相对路径,在新环境中如果这些文件不存在,可能触发额外的重新编译。
## 只读缓存(readonly)
只读模式适用于 CI 场景中使用预热缓存的构建任务。启用后,Rspack 只从磁盘读取缓存,不会写入新数据:
```javascript
module.exports = {
cache: {
type: 'persistent',
readonly: Boolean(process.env.CI)
}
}
```
这在多构建任务共享同一份缓存 artifact 时特别有用,可以避免并发写入导致的缓存损坏。
## 从 webpack 迁移缓存配置
如果你从 webpack 迁移到 Rspack,缓存配置需要做以下调整:
1. **缓存类型**:将 `cache.type: 'filesystem'` 改为 `cache.type: 'persistent'`
2. **构建依赖**:将 `buildDependencies` 从对象格式 `{ config: [__filename] }` 改为数组格式 `[__filename]`
3. **缓存版本**:将 webpack 的 `cache.name` 和 `cache.version` 合并为 Rspack 的 `cache.version`
4. **快照配置**:将顶层的 `snapshot` 配置移入 `cache.snapshot`
```javascript
// webpack 配置
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: { config: [__filename] },
name: 'my-app',
version: '1.0'
}
}
// Rspack 配置
module.exports = {
cache: {
type: 'persistent',
buildDependencies: [__filename],
version: 'my-app-1.0'
}
}
```
## 实际使用建议
**开发环境**:默认启用内存缓存即可满足需求。如果项目较大导致冷启动慢,可以同时开启持久化缓存。
**生产构建**:生产模式下缓存默认关闭。对于频繁构建的场景(如预发布环境),可以开启持久化缓存并将 `buildDependencies` 配置完整,确保配置变更时缓存正确失效。
**CI/CD 环境**:结合 `portable: true` 和 `readonly: true`,将构建产物缓存作为 pipeline artifact 上传和复用,可以显著减少 CI 构建时间。根据社区反馈,命中缓存后构建速度可以提升 2-3 倍。
**缓存失效排查**:如果遇到缓存未命中或构建结果异常,首先检查 `buildDependencies` 是否完整,其次确认 `version` 是否需要更新。Rspack 在 `cache.profile: true` 开启时会输出缓存统计信息,有助于定位问题。服务端5月27日 16:58
Rspack Loader 系统的工作原理是什么?Rspack 的 Loader 系统是其处理各类文件的核心机制。虽然 Rspack 基于 Rust 开发,但设计上充分考虑了与 Webpack Loader 生态的兼容性——大部分社区 Loader 可以直接使用,同时通过内置 Loader 提供了显著的性能优势。理解 Loader 系统的工作方式,是用好 Rspack 的关键前提。
## Loader 的角色与定位
Loader 是一种模块转换器,负责将源文件转换为 Rspack 能够处理的模块格式。它的核心能力包括:
- **语言编译**:将 TypeScript、JSX 等编译为 JavaScript
- **样式处理**:将 Less、Sass 编译为 CSS
- **资源管理**:处理图片、字体等静态资源
- **代码校验**:集成 ESLint 等代码检查工具
Rspack 中 Loader 的定位与 Webpack 一致:先通过 Loader 对模块进行预处理,转换为 Rspack 支持的模块类型,再根据 `rules[].type` 进行后置处理。这两步构成了完整的模块处理流水线。
## 配置方式
### 基本规则
在 `module.rules` 中通过 `test` 匹配文件扩展名,用 `use` 指定 Loader:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
}
]
}
}
```
### 多 Loader 链式调用
多个 Loader 组成链式处理,例如处理 CSS 文件:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
}
}
```
### 带选项的 Loader
通过 `options` 传递配置参数:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
}
```
## 执行顺序:从右到左与 Pitch 机制
Loader 链的执行顺序是理解整个系统的关键。
### 基本执行顺序
Loader 按**从右到左、从下到上**的顺序执行。对于 `use: ['style-loader', 'css-loader', 'sass-loader']`:
1. `sass-loader` 先执行,将 SCSS 编译为 CSS
2. `css-loader` 解析 CSS 中的 `@import` 和 `url()`
3. `style-loader` 将 CSS 注入 DOM
### Pitch 阶段
每个 Loader 除了正常的转换函数外,还可以定义一个 `pitch` 方法。Pitch 阶段**从左到右**执行,在所有正常阶段之前运行。当某个 Loader 的 pitch 方法返回了值,后续 Loader 的 pitch 和正常阶段都会被跳过,执行流程直接回溯到前一个 Loader。
对于 `use: ['a-loader', 'b-loader', 'c-loader']`,完整执行流程为:
```
a-loader pitch → b-loader pitch → c-loader pitch
→ c-loader normal → b-loader normal → a-loader normal
```
如果 `b-loader` 的 pitch 返回了值,则跳过 `c-loader` 和 `b-loader` 的 normal 阶段,直接进入 `a-loader` 的 normal 阶段。这个机制在 `style-loader` 的实现中被使用——它在 pitch 阶段拦截请求,直接返回一段将 CSS 注入页面的代码。
## 内置 Loader:Rspack 的性能利器
Rspack 提供了多个 Rust 实现的内置 Loader,在保持与 JavaScript Loader 相同可组合性的同时,提供了远超 JS Loader 的性能。
### builtin:swc-loader
基于 SWC 的超快 JavaScript/TypeScript 编译器,替代 `babel-loader` 和 `ts-loader`:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true
},
transform: {
react: {
runtime: 'automatic'
}
}
}
}
}
}
]
}
}
```
`builtin:swc-loader` 的核心优势在于 SWC 本身就是 Rust 编写的,因此这个 Loader 运行在 Rspack 的 Rust 进程内部,无需跨语言通信开销。
### builtin:lightningcss-loader
基于 Lightning CSS 的内置 CSS 处理器,可替代 `postcss-loader` + `autoprefixer` 的组合:
```javascript
import { rspack } from '@rspack/core';
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'builtin:lightningcss-loader',
options: {
targets: ['chrome 60', 'firefox 60']
}
}
]
}
]
}
}
```
主要配置项:
- **targets**:指定目标浏览器,支持 browserslist 查询字符串或版本号对象
- **errorRecovery**:默认 `true`,遇到无效 CSS 语法时跳过并发出警告而非中断编译
需要注意 Lightning CSS 严格遵循 CSS 规范,处理非标准 CSS(如 CSS Modules 的局部作用域语法)时可能出现兼容问题。
### builtin:css-loader
Rspack 还提供了 `builtin:css-loader` 作为 `css-loader` 的内置替代:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'builtin:css-loader']
}
]
}
}
```
### 原生 CSS 支持
除了内置 Loader,Rspack 还通过 `experiments.css` 提供原生 CSS 支持,CSS 作为一等公民被直接处理:
```javascript
module.exports = {
experiments: {
css: true
}
}
```
启用后可通过 `type: 'css'`、`type: 'css/auto'`、`type: 'css/global'`、`type: 'css/module'` 等模块类型控制 CSS 处理方式。此时不需要 `css-loader`,也不能与 `style-loader` 搭配使用,需使用 `CssExtractRspackPlugin` 或 Rspack 内置的 CSS 提取方案。
## 匹配规则与条件
### test / include / exclude
- **test**:正则匹配文件路径
- **include**:限定只处理指定目录
- **exclude**:排除指定目录
```javascript
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/,
use: 'babel-loader'
}
```
### oneOf
当同一类文件只需匹配一个规则时,使用 `oneOf` 避免重复处理:
```javascript
{
test: /\.css$/,
oneOf: [
{
resourceQuery: /module/,
use: 'css-loader?modules'
},
{
use: 'css-loader'
}
]
}
```
### resource 与 issuer
- **resource**:匹配被导入的资源路径
- **issuer**:匹配发起导入的模块路径
```javascript
{
test: /\.css$/,
issuer: /\.js$/, // 只处理从 JS 文件中导入的 CSS
use: 'style-loader'
}
```
## 自定义 Loader
### 同步 Loader
最简单的 Loader 形式,接收源码字符串,返回转换结果:
```javascript
module.exports = function(content) {
return content.toUpperCase();
};
```
### 异步 Loader
需要执行异步操作时,调用 `this.async()` 获取回调函数:
```javascript
module.exports = function(content) {
const callback = this.async();
someAsyncOperation(content).then(result => {
callback(null, result);
}).catch(err => {
callback(err);
});
};
```
### Raw Loader
默认情况下 Loader 接收 UTF-8 字符串。当需要处理二进制文件时,导出 `raw: true` 以接收 Buffer:
```javascript
module.exports = function(content) {
// content 是 Buffer
const size = content.length;
return `export default ${size}`;
};
module.exports.raw = true;
```
### 获取选项
通过 `this.getOptions()` 获取配置参数:
```javascript
module.exports = function(content, map, meta) {
const options = this.getOptions();
const result = content.replace(options.search, options.replace);
return result;
};
```
## Rspack Loader 架构:与 Webpack 的关键区别
Rspack 的 Loader 系统在设计上与 Webpack 保持了 API 兼容,但底层架构有本质区别。
### 旧架构:Rust 端的 Loader Runner
在早期版本中,Rspack 将 JS Loader 转换为可以从 Rust 端调用的原生函数,Loader Runner 完全运行在 Rust 端。这种方式的局限在于对复杂 JS Loader 的兼容性不够理想。
### 新架构:标识符驱动的调度机制
当前架构中,Rspack 从 Rust 核心将 Loader 请求委托给 JS 端的调度器,使用修改版的 webpack loader-runner 执行。每个 JS Loader 通过标识符(identifier)唯一识别,包含无法序列化字段的选项会复用 Loader 标识来避免重复解析。
### Rust-JS 通信优化
由于 Rspack 的核心构建逻辑在 Rust 端,而社区 Loader 运行在 JS 端,两者之间的通信是性能瓶颈。Rspack 的优化策略是将连续的 JS Loader 组合在一起执行,减少 Rust-JS 之间的通信轮次。当模块处理链中包含多个 JS Loader 时,Rspack 会把它们合并为一次 JS 调用,而非每个 Loader 都进行一次跨语言通信。
### 增量构建优势
与 esbuild 的 `onLoad` 回调不同,Rspack 在 rebuild 时只触发变动模块的 Loader 重复执行,未变化的模块直接使用缓存。在大项目中这是一个关键的 O(n) 复杂度优势。
## 性能优化建议
1. **优先使用内置 Loader**:`builtin:swc-loader` 替代 `babel-loader`,`builtin:lightningcss-loader` 替代 `postcss-loader`,性能提升通常在数倍到数十倍
2. **缩小 Loader 作用范围**:使用 `include` 和 `exclude` 避免对 `node_modules` 等目录执行不必要的转换
3. **启用缓存**:对 `babel-loader` 等仍需使用的 JS Loader 开启 `cacheDirectory`
4. **使用原生 CSS 方案**:如果项目不依赖 `css-loader` 的特定功能,启用 `experiments.css` 可以获得更好的 CSS 处理性能
5. **避免不必要的 Loader**:Rspack 原生支持 Asset Modules,不需要 `file-loader` 和 `url-loader`,通过 `type: 'asset/resource'` 和 `type: 'asset'` 替代
## 兼容性说明
Rspack 支持绝大多数社区 Loader,只有少数依赖 Webpack 内部实现细节的 Loader(如 `cache-loader`)尚未兼容。如果遇到不兼容的情况,可以通过 [Rspack GitHub Issues](https://github.com/web-infra-dev/rspack/issues) 反馈。前端5月27日 16:55
Rspack 如何处理 CSS?Rspack 将 CSS 视为一等公民,内置了完整的 CSS 处理能力,无需像 Webpack 那样依赖 css-loader 和 style-loader。理解 Rspack 的 CSS 处理机制,是从 Webpack 迁移或新建项目时的关键知识。
## CSS 模块类型
Rspack 通过 `module.rules` 中的 `type` 字段来区分 CSS 的处理方式,支持四种模块类型:
| 类型 | 说明 |
|------|------|
| `css/auto` | 根据文件名自动判断:`*.module.css` 视为 CSS Modules,其余视为普通 CSS |
| `css` | 普通 CSS,不启用 CSS Modules |
| `css/global` | 以全局作用域模式解析 CSS Modules |
| `css/module` | 强制启用 CSS Modules |
从 Rspack 0.6.0 起,`*.css` 文件默认类型从 `css` 变更为 `css/auto`,这意味着 `style.css` 和 `style.module.css` 可以在同一项目中自动区分处理,无需额外配置:
```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
type: 'css/auto', // 默认值,可省略
},
],
},
};
```
## CSS Modules
Rspack 内置支持 CSS Modules,无需 css-loader。以 `.module.css` 结尾的文件会被自动识别:
```css
/* index.module.css */
.container {
display: flex;
}
.active {
color: red;
}
```
在 JavaScript 中通过命名空间导入使用:
```js
import * as styles from './index.module.css';
// 使用
document.querySelector('.'app').className = styles.container;
```
也支持命名导入:
```js
import { active } from './index.module.css';
```
如果需要默认导入(`import styles from './index.module.css'`),需要关闭 `namedExports`:
```js
module.exports = {
module: {
rules: [
{
test: /\.module\.css$/i,
type: 'css/auto',
use: [{
loader: 'css-loader',
options: { modules: { namedExports: false } },
}],
},
],
},
};
```
## CSS 提取到独立文件
生产环境通常需要将 CSS 提取到独立文件。Rspack 提供了内置的 `CssExtractRspackPlugin`,替代 Webpack 中的 `mini-css-extract-plugin`:
```js
import { rspack } from '@rspack/core';
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
type: 'css/auto',
},
],
},
plugins: [
new rspack.CssExtractRspackPlugin({
filename: 'css/[name].[contenthash].css',
chunkFilename: 'css/[id].[contenthash].css',
}),
],
};
```
注意:`CssExtractRspackPlugin` 与 Rspack 内置 CSS 类型(`css/auto`、`css`、`css/module`)配合使用,不需要像 Webpack 那样在 loader 链中手动注入提取 loader。
如果项目仍依赖 css-loader,可以使用传统方式:
```js
import { rspack } from '@rspack/core';
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
type: 'javascript/auto', // 覆盖内置 CSS 类型
use: [
rspack.CssExtractRspackPlugin.loader,
'css-loader',
],
},
],
},
plugins: [
new rspack.CssExtractRspackPlugin({}),
],
};
```
## CSS 预处理器
Rspack 通过对应的 loader 支持主流 CSS 预处理器,处理结果交给 Rspack 内置 CSS 引擎进行后处理。
### Sass/SCSS
```js
module.exports = {
module: {
rules: [
{
test: /\.s(?:a|c)ss$/,
type: 'css/auto', // 自动识别 *.module.scss
use: ['sass-loader'],
},
],
},
};
```
### Less
```js
module.exports = {
module: {
rules: [
{
test: /\.less$/,
type: 'css/auto', // 自动识别 *.module.less
use: ['less-loader'],
},
],
},
};
```
### Stylus
```js
module.exports = {
module: {
rules: [
{
test: /\.styl$/,
type: 'css/auto',
use: ['stylus-loader'],
},
],
},
};
```
关键区别:与 Webpack 不同,Rspack 不需要在 loader 链中加入 css-loader 和 style-loader,预处理器 loader 的输出直接由 Rspack 内置 CSS 引擎接管。
## PostCSS 与 Tailwind CSS 集成
Rspack 通过 postcss-loader 集成 PostCSS 生态,这是接入 Tailwind CSS 等工具的标准方式。
### 基础 PostCSS 配置
```js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
type: 'css/auto',
use: [
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('autoprefixer'),
require('cssnano')({ preset: 'default' }),
],
},
},
},
],
},
],
},
};
```
也可以使用独立的 `postcss.config.js` 配置文件:
```js
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')({
overrideBrowserslist: ['> 1%', 'last 2 versions'],
}),
],
};
```
### Tailwind CSS v4 集成
Tailwind CSS v4 采用了新的 PostCSS 插件架构:
```bash
npm install tailwindcss @tailwindcss/postcss postcss postcss-loader -D
```
```js
// postcss.config.mjs
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
```
```js
// rspack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['postcss-loader'],
type: 'css',
},
],
},
};
```
## CSS 优化
Rspack 内置了 CSS 优化能力,生产模式下默认启用:
**代码压缩**:Rspack 使用内置压缩器处理 CSS,也可以通过 `CssMinimizerPlugin` 自定义压缩策略:
```js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new CssMinimizerPlugin({
minimizerOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
}),
],
},
};
```
**Tree Shaking**:Rspack 在内置 CSS 处理中支持未使用 CSS 的移除,分析 JavaScript 中的类名引用,只保留实际使用的样式规则。
**代码分割**:配合 `splitChunks` 可以将 CSS 按策略拆分:
```js
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
styles: {
type: 'css/mini-extract',
name: 'styles',
chunks: 'all',
enforce: true,
},
},
},
},
};
```
## 从 Webpack 迁移 CSS 配置
迁移时需要注意的核心差异:
1. **移除 css-loader 和 style-loader**:Rspack 内置了 CSS 处理,这两个 loader 不再需要
2. **替换 mini-css-extract-plugin**:使用内置的 `rspack.CssExtractRspackPlugin`
3. **设置模块类型**:通过 `type: 'css/auto'` 替代 loader 链方式控制 CSS Modules 行为
4. **experiments.css**:在 Rspack 2.0 中内置 CSS 支持默认启用,旧版本可通过 `experiments: { css: true }` 开启
```js
// Webpack 配置 → Rspack 配置
// 之前:
// { test: /\.css$/, use: ['style-loader', 'css-loader'] }
// 之后:
{ test: /\.css$/i, type: 'css/auto' }
```
这种简化得益于 Rspack 用 Rust 实现的内置 CSS 解析管线,避免了 Webpack 中多 loader 串联的性能开销。前端5月27日 16:54
Rspack 环境变量怎么配置和管理?Rspack 的环境变量管理是前端工程化中的核心能力,用于区分开发、测试、生产等不同环境的配置。本文从 Rspack 原生插件、Rsbuild 集成、.env 文件、多环境配置、TypeScript 类型安全到 CI/CD 实践,系统讲解环境变量的完整管理方案。
## 环境变量的作用
环境变量在构建时注入到代码中,被 Rspack 直接替换为字面量值。这意味着代码中引用 `process.env.NODE_ENV` 的地方,打包后会被替换为 `"production"` 或 `"development"` 字符串,而非运行时读取。这一机制既能实现条件编译,也能配合 tree shaking 移除死代码。
## Rspack 原生插件配置
### DefinePlugin
Rspack 内置 `DefinePlugin`,用法与 webpack 一致,用于将代码中的标识符替换为给定值:
```javascript
const { DefinePlugin } = require('@rspack/core');
module.exports = {
plugins: [
new DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.API_URL': JSON.stringify('https://api.example.com'),
'process.env.VERSION': JSON.stringify('1.0.0')
})
]
};
```
值的格式要求:所有值必须用 `JSON.stringify()` 包裹,因为插件做的是文本替换,不包裹会变成未定义的变量引用。
### EnvironmentPlugin
`EnvironmentPlugin` 是 `DefinePlugin` 针对 `process.env` 的语法糖,直接读取系统环境变量:
```javascript
const { EnvironmentPlugin } = require('@rspack/core');
module.exports = {
plugins: [
new EnvironmentPlugin({
NODE_ENV: 'development', // 默认值,process.env 中没有时使用
DEBUG: false, // 默认值
API_KEY: undefined // undefined 表示必须提供,否则构建报错
})
]
};
```
与 `DefinePlugin` 的区别:`EnvironmentPlugin` 自动从 `process.env` 读取值并应用 `JSON.stringify`,无需手动处理。用 `undefined` 作默认值时,变量缺失会报错;用 `null` 作默认值则变量缺失时静默跳过。
## Rsbuild 中的环境变量
Rsbuild 基于 Rspack 封装,提供了更简洁的环境变量管理方式。
### 默认注入的变量
Rsbuild 自动注入以下变量,无需手动配置:
```javascript
// import.meta.env 中可用
import.meta.env.MODE // 构建模式:'production' | 'development' | 'none'
import.meta.env.DEV // 是否为开发模式
import.meta.env.PROD // 是否为生产模式
import.meta.env.SSR // 是否为 SSR 模式
import.meta.env.BASE_URL // 基础 URL
import.meta.env.ASSET_PREFIX // 资源前缀
// process.env 中可用
process.env.NODE_ENV // 自动设为 'development' 或 'production'
process.env.BASE_URL
process.env.ASSET_PREFIX
```
### source.define 自定义变量
通过 `source.define` 配置项注入自定义变量,这是 Rsbuild 推荐的方式:
```javascript
export default {
source: {
define: {
'process.env.CUSTOM_VAR': JSON.stringify('value'),
'import.meta.env.LANGUAGE': JSON.stringify('zh-CN'),
},
},
};
```
### 关闭 NODE_ENV 自动注入
如果需要自定义 `process.env.NODE_ENV` 的行为,通过 Rspack 的 `optimization.nodeEnv` 控制:
```javascript
export default {
tools: {
rspack: {
optimization: { nodeEnv: false },
},
},
};
```
### 手动加载环境变量
使用 Rsbuild 的 JavaScript API(非 CLI)时,需要手动调用 `loadEnv`:
```javascript
import { loadEnv, mergeRsbuildConfig } from '@rsbuild/core';
const { parsed, publicVars } = loadEnv();
const mergedConfig = mergeRsbuildConfig(
{
source: {
define: publicVars,
},
},
userConfig,
);
```
## .env 文件管理
### 文件加载规则
Rsbuild CLI 自动使用 dotenv 加载项目根目录的 `.env` 文件,加载优先级从高到低:
1. `.env.[mode].local` — 特定环境的本地覆盖(不提交到 Git)
2. `.env.local` — 本地覆盖(不提交到 Git)
3. `.env.[mode]` — 特定环境的共享配置
4. `.env` — 所有环境的默认值
以 `PUBLIC_` 为前缀的变量会暴露到客户端代码中,其他变量仅在 Node 侧可用。可以通过 `--no-env` 选项禁用自动加载。
### 文件命名示例
```bash
# .env — 通用默认值
PUBLIC_APP_TITLE=MyApp
# .env.development — 开发环境
PUBLIC_API_URL=http://localhost:3000
DEBUG=true
# .env.production — 生产环境
PUBLIC_API_URL=https://api.example.com
DEBUG=false
# .env.local — 本地覆盖(加入 .gitignore)
API_KEY=your-secret-key
```
### .gitignore 配置
```gitignore
.env.local
.env.*.local
```
提交 `.env.development` 和 `.env.production` 方便团队共享,而 `.local` 后缀的文件仅用于本地敏感配置。
## 命令行传递环境变量
直接通过命令行设置环境变量:
```bash
# Unix/Linux/macOS
NODE_ENV=production API_URL=https://api.example.com npx rspack build
# Windows(cmd)
set NODE_ENV=production&& set API_URL=https://api.example.com&& npx rspack build
# 跨平台方案
npx cross-env NODE_ENV=production npx rspack build
```
Rsbuild CLI 还支持 `--env` 参数传递:
```bash
npx rsbuild build --env production
```
## 环境变量与 Tree Shaking
环境变量的文本替换特性可以标记死代码,帮助 Rspack 在构建时移除不需要的分支:
```javascript
// 源码
if (import.meta.env.DEV) {
console.log('debug info');
}
// 生产构建后,整个 if 分支被移除
```
利用这一机制,可以通过自定义变量实现条件编译:
```javascript
export default {
source: {
define: {
'import.meta.env.LANGUAGE': JSON.stringify('zh-CN'),
},
},
};
// 代码中
if (import.meta.env.LANGUAGE === 'zh-CN') {
// 仅中文版包含的代码
}
```
## TypeScript 类型定义
为环境变量添加类型声明,避免拼写错误和类型不安全:
```typescript
// env.d.ts
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
API_URL: string;
VERSION: string;
DEBUG: string;
}
}
// 或为 import.meta.env 添加类型(Rsbuild 项目)
interface ImportMetaEnv {
readonly MODE: string;
readonly DEV: boolean;
readonly PROD: boolean;
readonly CUSTOM_VAR: string;
}
```
## 多环境配置方案
### 方案一:配置文件拆分
将通用配置和各环境配置拆分为独立文件,通过函数导出按环境合并:
```javascript
// rspack.config.js
module.exports = (env) => {
const mode = env.mode || 'development';
const envConfig = require(`./rspack.${mode}.js`);
return { ...commonConfig, ...envConfig };
};
```
```javascript
// rspack.development.js
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: { hot: true, port: 3000 }
};
// rspack.production.js
module.exports = {
mode: 'production',
devtool: 'source-map',
optimization: { minimize: true }
};
```
### 方案二:条件配置
在同一配置文件中根据环境变量条件切换:
```javascript
module.exports = (env) => {
const isProduction = env.mode === 'production';
return {
mode: isProduction ? 'production' : 'development',
plugins: [
new DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(env.mode),
'process.env.IS_PRODUCTION': JSON.stringify(isProduction)
}),
...isProduction ? [
new TerserPlugin(),
new CompressionPlugin()
] : []
]
};
};
```
## 环境变量的安全实践
### 敏感信息处理
```javascript
// 错误:硬编码密钥
const API_KEY = 'sk-xxxxx';
// 正确:从环境变量读取
const API_KEY = process.env.API_KEY;
```
敏感信息存放在 `.env.local` 或 CI/CD 的 secrets 中,不提交到版本控制。
### 必需变量验证
```javascript
const requiredVars = ['API_URL', 'API_KEY'];
requiredVars.forEach(key => {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
});
```
### 默认值设置
```javascript
const apiUrl = process.env.API_URL || 'http://localhost:3000';
const timeout = parseInt(process.env.TIMEOUT || '5000', 10);
const debug = process.env.DEBUG === 'true';
```
## CI/CD 中的环境变量
### GitHub Actions
```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install
- run: npm run build
env:
NODE_ENV: production
API_URL: ${{ secrets.API_URL }}
```
### Docker
```dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
ARG NODE_ENV=production
ARG API_URL
ENV NODE_ENV=$NODE_ENV
ENV API_URL=$API_URL
RUN npm run build
```
### Rspack 性能分析
从 Rspack 1.4 开始,通过 `RSPACK_PROFILE` 环境变量开启构建性能追踪:
```bash
RSPACK_PROFILE=true npx rspack build
```
Rspack 的环境变量管理覆盖了从开发调试到生产部署的完整链路:`DefinePlugin` 和 `EnvironmentPlugin` 提供底层注入能力,Rsbuild 的 `source.define` 和自动 `.env` 加载简化日常使用,`import.meta.env` 系列变量开箱即用,配合 TypeScript 类型声明和条件编译可以构建类型安全、产物精简的前端项目。前端5月27日 16:54
Rspack Dev Server 如何配置和使用?Rspack Dev Server 为本地开发提供热更新、代理、HTTPS 等能力,是日常开发的核心工具。Rspack 2.0 对 Dev Server 做了较大重构:底层从 Express 切换到 connect-next,`@rspack/cli` 不再自动依赖 `@rspack/dev-server`,需要手动安装。本文基于 Rspack 2.0,系统讲解 Dev Server 的配置和使用。
## 安装与启动
Rspack 2.0 起,`@rspack/dev-server` 是可选依赖,需手动安装:
```bash
npm add @rspack/dev-server -D
```
启动开发服务器有两种方式:
```bash
# 方式一:rspack dev(推荐)
npx rspack dev
# 方式二:rspack serve(兼容写法)
npx rspack serve
```
指定配置文件或端口:
```bash
npx rspack dev --config rspack.config.js --port 8080
```
在配置文件中声明 Dev Server 选项:
```javascript
// rspack.config.js
module.exports = {
mode: 'development',
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 9000,
},
};
```
## 核心功能
### 模块热更新(HMR)
HMR 默认在 development 模式下启用,修改代码后页面局部刷新而不需要整页重载:
```javascript
module.exports = {
devServer: {
hot: true, // 启用 HMR(默认开启)
liveReload: false, // 禁用整页自动刷新
},
};
```
注意:当 `output.cssFilename` 包含 `[hash]` 或 `[contenthash]` 时,CSS 的 HMR 不会生效。
### 静态文件服务
Dev Server 可以为静态资源提供文件服务:
```javascript
module.exports = {
devServer: {
static: {
directory: path.join(__dirname, 'public'),
publicPath: '/',
serveIndex: true,
watch: true,
},
},
};
```
`watch: true` 会在静态文件变化时自动刷新页面,`serveIndex: true` 则允许浏览目录列表。
### 代理配置
开发环境常需解决跨域问题,Dev Server 内置了基于 `http-proxy-middleware` 的代理:
```javascript
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: { '^/api': '' },
},
},
},
};
```
也支持数组格式,适合更复杂的匹配场景:
```javascript
module.exports = {
devServer: {
proxy: [
{
context: ['/api', '/graphql'],
target: 'http://localhost:3000',
changeOrigin: true,
},
],
},
};
```
### HTTPS 支持
本地开发需要 HTTPS 时(如测试 Service Worker、Secure Cookie 等):
```javascript
const fs = require('fs');
module.exports = {
devServer: {
server: {
type: 'https',
options: {
key: fs.readFileSync('path/to/private.key'),
cert: fs.readFileSync('path/to/certificate.pem'),
},
},
},
};
```
## 高级配置
### 错误覆盖与客户端日志
Dev Server 可以在浏览器中实时显示编译错误,辅助快速定位问题:
```javascript
module.exports = {
devServer: {
client: {
overlay: {
errors: true,
warnings: false,
},
logging: 'warn',
},
},
};
```
### historyApiFallback
SPA 应用需要将所有路由回退到 index.html,避免刷新页面时 404:
```javascript
module.exports = {
devServer: {
historyApiFallback: {
index: '/index.html',
rewrites: [
{ from: /^\/api/, to: '/404.html' },
],
},
},
};
```
### 文件监听
当需要监听源码之外的文件变化(如 PHP 模板、公共资源)时:
```javascript
module.exports = {
devServer: {
watchFiles: {
paths: ['src/**/*.php', 'public/**/*'],
options: {
usePolling: false,
interval: 1000,
},
},
},
};
```
### devMiddleware 选项
控制构建输出的写入和日志行为:
```javascript
module.exports = {
devServer: {
devMiddleware: {
index: true,
writeToDisk: false,
stats: 'minimal',
},
},
};
```
`writeToDisk: false` 表示构建产物仅存在内存中,加快速度;`stats` 控制终端日志的详细程度,可选 `none`、`errors-only`、`minimal`、`normal`、`verbose`。
## 自定义中间件
### Rspack 2.0 的 connect-next 适配
Rspack 2.0 将底层从 Express 替换为 connect-next,中间件写法需要适配:
```javascript
module.exports = {
devServer: {
setupMiddlewares: (middlewares, devServer) => {
if (!devServer) {
throw new Error('devServer is not defined');
}
devServer.app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
return middlewares;
},
},
};
```
如果项目仍依赖 Express 特有 API(如 `req.query` 的解析行为),可以显式提供 Express 实例:
```javascript
import express from 'express';
export default {
devServer: {
app: async () => express(),
},
};
```
### WebSocket 配置
自定义 WebSocket 连接地址和服务端类型:
```javascript
module.exports = {
devServer: {
client: {
webSocketURL: 'auto://0.0.0.0:0/ws',
},
webSocketServer: {
type: 'ws',
options: {
host: 'localhost',
port: 8080,
},
},
},
};
```
也支持使用自定义 WebSocket 客户端实现,继承 `BaseClient` 类即可。
## 压缩与性能
启用 gzip 压缩减少传输体积:
```javascript
module.exports = {
devServer: {
compress: true,
},
};
```
配合 devMiddleware 的 stats 选项保持终端输出简洁,能显著提升开发体验。
## 从 Rspack 1.x 迁移
Rspack 2.0 的 Dev Server 有几项破坏性变更需要注意:
- **必须手动安装 `@rspack/dev-server`**:`@rspack/cli` 不再自动包含此依赖
- **底层切换为 connect-next**:如果使用了 Express 专有 API,需显式提供 `app: async () => express()`
- **Node.js 版本要求**:Rspack 2.0 要求 Node.js 20.19+ 或 22.12+,不再支持 Node 18
- **ESM-only 发布**:`@rspack/dev-server` 已移除 CommonJS 构建,纯 ESM 包
## 最佳实践
- **开发/生产分离**:Dev Server 仅用于开发环境,生产部署使用静态文件服务器
- **代理环境变量化**:代理目标地址通过环境变量管理,避免硬编码
- **按需监听文件**:`watchFiles` 只监听必要路径,避免不必要的重编译
- **合理配置日志**:生产前关闭 overlay,日常开发使用 `errors-only` 或 `minimal`
- **HTTPS 按需开启**:仅在需要安全上下文(Service Worker、Secure Cookie 等)时配置 HTTPS
服务端5月27日 16:53
Rspack 的性能监控和调试是如何工作的?Rspack 的性能监控和调试涉及构建耗时分析、产物体积优化和运行时问题排查。以下从核心工具、配置方法和实战技巧三个维度展开。
## 构建性能分析
### RSPACK_PROFILE 环境变量
Rspack 内置了基于环境变量的性能分析能力,无需安装额外依赖:
```bash
# 生成构建各阶段耗时日志
RSPACK_PROFILE=ALL rspack build
```
执行后在项目根目录生成 `.rspack-profile-${timestamp}-${pid}` 文件夹,其中 `rspack.log` 记录了构建各阶段的耗时数据,可以直接定位耗时最长的环节。
如果需要更细粒度的分析,可以使用 Perfetto 生成可视化 trace:
```bash
RSPACK_PROFILE=TRACE=layer=perfetto rspack build
```
这会生成 `rspack.pftrace` 文件,上传到 [ui.perfetto.dev](https://ui.perfetto.dev/) 即可以时间轴形式查看每个构建步骤的耗时分布。
还可以按模块过滤 trace,只关注特定环节:
```bash
# 只看编译阶段的 trace
RSPACK_PROFILE='TRACE=layer=logger&filter=rspack_core::compiler::compilation' rspack build
# 只看 chunk 图构建
RSPACK_PROFILE='TRACE=filter=[build_chunk_graph]' rspack build
# 调试模块解析
RSPACK_PROFILE='TRACE=filter=rspack_resolver=trace&layer=logger' rspack build
```
### Rsdoctor 构建分析工具
[Rsdoctor](https://rsdoctor.dev/) 是 Rspack 生态专用的构建分析工具,可以直观展示编译时间、编译前后代码变化、模块引用关系和重复模块:
```bash
npm install @rsdoctor/rspack-plugin -D
```
```javascript
const { RsdoctorPlugin } = require('@rsdoctor/rspack-plugin');
module.exports = {
plugins: [
new RsdoctorPlugin({
// 构建完成后自动打开分析报告
disableClientServer: false,
}),
],
};
```
构建完成后会自动打开浏览器展示分析面板,包含模块编译耗时排行、重复依赖检测和产物体积分布等视图。
### Stats JSON 输出
通过 `--profile` 参数输出构建统计:
```bash
npx rspack build --profile --json > stats.json
```
配合 webpack-bundle-analyzer 可视化分析产物体积:
```javascript
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'bundle-report.html',
}),
],
};
```
## 构建统计配置
Rspack 的 `stats` 选项控制构建输出的信息粒度:
```javascript
module.exports = {
stats: {
colors: true,
timings: true, // 显示各阶段耗时
builtAt: true, // 构建时间戳
assets: true, // 输出资源列表
assetsSort: 'size', // 按体积排序
modules: true, // 模块信息
chunks: true, // chunk 信息
reasons: true, // 模块引入原因
errors: true,
errorDetails: true,
warnings: true,
// 日志级别控制
logging: 'warn',
loggingDebug: /rspack/,
// 缓存命中信息
cached: true,
cachedAssets: true,
},
};
```
`logging` 和 `loggingDebug` 配合使用可以在 warn 级别下单独开启特定模块的详细日志,减少无关输出。
## Source Map 与开发调试
### Source Map 配置
```javascript
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
};
```
`eval-cheap-module-source-map` 在开发模式下兼顾重建速度和调试体验。生产环境建议使用 `hidden-source-map` 配合错误监控平台使用。
### 开发服务器错误覆盖
```javascript
module.exports = {
devServer: {
client: {
overlay: {
errors: true,
warnings: false,
},
},
},
};
```
开启后编译错误会直接覆盖在浏览器页面上,无需切换到终端查看。
## 缓存与线程池调优
### 文件系统缓存
```javascript
module.exports = {
cache: {
type: 'filesystem',
profile: true, // 输出缓存耗时信息
},
};
```
开启 `profile: true` 后,stats 中会包含缓存命中和未命中的耗时对比,帮助判断缓存效果。
### 线程池配置
Rspack 底层使用 Tokio(异步 I/O)和 Rayon(CPU 密集任务)两套线程池,可通过环境变量调整:
```bash
# 限制工作线程数,适用于 CI 等资源受限环境
TOKIO_WORKER_THREADS=4 RAYON_NUM_THREADS=4 rspack build
# 调整阻塞线程池大小(默认为 4)
RSPACK_BLOCKING_THREADS=2 rspack build
```
线程数并非越大越好。在容器化部署或共享 CI 机器上,过高的并行度反而会因为线程上下文切换导致性能下降。
## 常见性能问题与解决
### 构建速度慢
**排查思路**:先用 `RSPACK_PROFILE=ALL` 或 Rsdoctor 定位耗时环节,再针对性优化。
**典型原因与方案**:
- JS Loader 开销大:用 Rspack 内置的 `builtin:swc-loader` 替代 `babel-loader`,用 `builtin:lightningcss-loader` 替代 `postcss-loader`,可显著减少 Loader 编译耗时
- 缓存未启用:开启 `cache: { type: 'filesystem' }`
- 模块解析路径过长:配置 `resolve.modules` 和 `resolve.extensions` 减少查找范围
- Loader 作用范围过大:用 `rules[].include` 限定 Loader 只处理必要的文件
```javascript
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
module: {
rules: [
{
test: /\.tsx?$/,
include: path.resolve(__dirname, 'src'),
use: 'builtin:swc-loader',
},
],
},
};
```
### 内存占用高
- Source Map 配置过高:开发环境用 `eval-cheap-source-map` 而非 `source-map`
- 并行度过高:降低 `parallelism` 值或调整 `RAYON_NUM_THREADS`
- 关闭不必要的优化:`optimization.removeAvailableModules: false` 可减少二次处理
```javascript
module.exports = {
devtool: 'eval-cheap-source-map',
parallelism: 4,
optimization: {
removeAvailableModules: false,
removeEmptyChunks: false,
},
};
```
### 打包体积大
- 开启 Tree Shaking:`optimization.usedExports: true` + `optimization.sideEffects: true`(需在 package.json 中标记 `"sideEffects": false`)
- 配置代码分割:使用 `splitChunks` 将第三方库分离
- 按需加载:使用动态 `import()` 实现路由级懒加载
```javascript
module.exports = {
optimization: {
usedExports: true,
sideEffects: true,
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
},
},
},
},
};
```
## 自定义调试插件
当内置工具无法满足需求时,可以通过 Rspack 的 Hook 机制编写调试插件:
```javascript
class DebugPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('DebugPlugin', (compilation) => {
console.log('[Debug] Compilation started');
});
compiler.hooks.done.tap('DebugPlugin', (stats) => {
const { errors, warnings } = stats.compilation;
console.log(`[Debug] Build completed — errors: ${errors.length}, warnings: ${warnings.length}`);
});
}
}
module.exports = {
plugins: [new DebugPlugin()],
};
```
逐步调试法也是常用手段:先用最小配置启动构建确认基线正常,再逐步添加 Loader、Plugin 和优化选项,每次只加一项,出问题时立即定位到新增配置。
## 性能基准与持续监控
### 建立构建性能基准
```javascript
const { execSync } = require('child_process');
function benchmark() {
const iterations = 5;
const times = [];
for (let i = 0; i < iterations; i++) {
const start = Date.now();
execSync('npx rspack build');
times.push(Date.now() - start);
}
const avg = times.reduce((a, b) => a + b, 0) / iterations;
console.log(`Avg: ${avg}ms, Min: ${Math.min(...times)}ms, Max: ${Math.max(...times)}ms`);
}
benchmark();
```
将基准数据纳入 CI 流程,当构建耗时出现显著回退时自动告警,比人工感知更可靠。
### 生成性能报告
```javascript
const fs = require('fs');
const path = require('path');
function generateReport(stats) {
const report = {
timestamp: new Date().toISOString(),
buildTime: stats.time,
modules: stats.modules.length,
assets: stats.assets.map((a) => ({ name: a.name, size: a.size })),
warnings: stats.warnings.length,
errors: stats.errors.length,
};
fs.writeFileSync(
path.join(__dirname, 'performance-report.json'),
JSON.stringify(report, null, 2)
);
}
```
定期收集性能报告并对比历史数据,可以及时发现构建性能的劣化趋势。
Rspack 的性能监控与调试体系以 RSPACK_PROFILE 和 Rsdoctor 为核心,配合 Source Map、缓存、线程池调优以及自定义插件,覆盖了从定位瓶颈到验证优化的完整链路。掌握这些工具的关键在于先用分析工具定位问题,再针对性地调整配置,避免盲目优化。前端5月27日 16:53
Rspack 如何支持 TypeScript?内置 SWC 编译与类型检查配置Rspack 通过内置的 SWC 编译器为 TypeScript 提供开箱即用的支持,无需安装 ts-loader 或 babel-loader 等额外依赖,直接导入 `.ts` 和 `.tsx` 文件即可完成编译。下面从配置方法、SWC Loader 选项、tsconfig.json 集成、类型检查策略以及常见问题几个方面展开说明。
## 基本配置
### 最小可运行配置
一个最简单的 Rspack + TypeScript 项目只需要以下配置:
```javascript
// rspack.config.js
module.exports = {
entry: './src/index.ts',
module: {
rules: [
{
test: /\.ts$/,
use: 'builtin:swc-loader',
type: 'javascript/auto',
},
],
},
resolve: {
extensions: ['.ts', '.js'],
},
};
```
`builtin:swc-loader` 是 Rspack 内置的 SWC 加载器,不需要额外安装。SWC 用 Rust 编写,编译速度比 Babel 快 20-70 倍,比 tsc 快 10-30 倍,同时内存占用更低。
### 支持 JSX 的完整配置
当项目使用 React + TypeScript 时,需要启用 TSX 解析和 React 自动运行时:
```javascript
// rspack.config.js
module.exports = {
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
decorators: true,
dynamicImport: true,
},
transform: {
react: {
runtime: 'automatic',
},
},
},
},
},
type: 'javascript/auto',
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
};
```
这里 `type: 'javascript/auto'` 不可省略,它告诉 Rspack 将 TypeScript 文件作为普通 JavaScript 模块处理。`decorators: true` 启用装饰器语法支持,`dynamicImport: true` 启用动态 import() 语法。
## SWC Loader 进阶配置
### 编译目标与产物体积
SWC 默认可能将代码降级到 ES5,导致产物体积偏大。建议显式指定 `target` 以控制降级范围:
```javascript
{
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
importSource: '@emotion/react', // 配合 CSS-in-JS 库
},
},
target: 'es2022', // 避免不必要的降级,减小产物体积
externalHelpers: true, // 将 helper 函数抽取为外部依赖
},
env: {
targets: '> 0.25%, not dead',
coreJs: 3,
},
sourceMaps: true,
},
}
```
`target: 'es2022'` 让 SWC 只在必要时降级语法,比默认的 ES5 产物小很多。`externalHelpers: true` 将 `__spreadArray`、`__awaiter` 等运行时辅助函数抽取到共享模块中,避免每个文件内联一份。`env` 选项配合 `coreJs` 可按需注入 polyfill。
### 全局变量替换
在构建时替换环境变量可以消除开发代码:
```javascript
jsc: {
optimizer: {
globals: {
vars: {
'process.env.NODE_ENV': '"production"',
},
},
},
}
```
配合 Dead Code Elimination,`if (process.env.NODE_ENV === 'development')` 中的代码块会被完全移除。
## tsconfig.json 集成
Rspack 不直接读取 tsconfig.json 来决定编译行为(这是 SWC 的工作),但 TypeScript 语言服务和类型检查器依赖它。一个典型的 Rspack 项目 tsconfig.json 如下:
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules"]
}
```
几个关键配置说明:
- **`isolatedModules: true`**:必须开启。Rspack/SWC 逐文件编译,不做跨模块类型分析,这个选项让 tsc 的行为与 Rspack 对齐,避免导出类型但未实际使用的场景下出现运行时错误。
- **`noEmit: true`**:让 tsc 只做类型检查,不输出文件,因为编译工作由 Rspack 完成。
- **`moduleResolution: "bundler"`**:适用于 Rspack 这类打包工具的模块解析策略,支持 package.json 的 exports 字段。
- **`paths`**:路径别名映射,但注意这仅影响 tsc 的类型解析,Rspack 的模块解析需要单独配置 `resolve.alias`。
### 路径别名在 Rspack 中的配置
tsconfig.json 中的 `paths` 不会自动生效于 Rspack 的模块解析,需要在 rspack.config.js 中添加对应的 alias:
```javascript
const path = require('path');
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
};
```
两边配置需要保持一致,否则编辑器中不报错但构建时找不到模块。
## 类型检查
SWC 只做语法转译,不执行类型检查。类型检查需要额外方案:
### 方案一:ForkTsCheckerWebpackPlugin
在构建过程中并行执行类型检查,不影响编译速度:
```javascript
const rspack = require('@rspack/core');
module.exports = {
plugins: [
new rspack.ForkTsCheckerWebpackPlugin({
typescript: {
configFile: './tsconfig.json',
memoryLimit: 4096, // MB,大型项目可能需要调高
},
}),
],
};
```
开发模式下该插件不会阻塞构建,类型错误会以 overlay 或终端警告的形式展示;生产构建时会阻塞,类型错误将导致构建失败。
### 方案二:独立运行 tsc
在 CI/CD 中用 `tsc --noEmit` 单独执行类型检查,与构建过程完全解耦:
```json
{
"scripts": {
"build": "rspack build",
"typecheck": "tsc --noEmit",
"ci": "npm run typecheck && npm run build"
}
}
```
这种方式更灵活,类型检查不影响构建性能,适合大型项目。
### 类型检查的最佳实践
- **开发环境**:使用编辑器内置的 TypeScript 语言服务做实时检查即可,不需要在 dev server 中运行 ForkTsCheckerWebpackPlugin,避免拖慢 HMR 速度。
- **生产构建**:启用完整类型检查,阻止类型错误的代码部署。
- **CI/CD**:将 `tsc --noEmit` 作为独立步骤,与构建并行执行,缩短流水线耗时。
## rspack.config.ts — 用 TypeScript 写配置
从 Rspack v1.5.0 开始,CLI 内置了对 TypeScript 配置文件的支持。使用 `rspack.config.ts` 可以获得完整的类型提示:
```typescript
import type { Configuration } from '@rspack/core';
const config: Configuration = {
entry: './src/index.tsx',
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: {
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: { runtime: 'automatic' },
},
},
},
},
type: 'javascript/auto',
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
};
export default config;
```
Rspack CLI 默认使用 `--configLoader=auto`,会优先尝试原生 TypeScript 支持,失败则回退到 Jiti 转译。
## 常见问题
### 类型声明文件找不到
安装对应的 `@types` 包即可,例如 `npm install -D @types/lodash`。如果使用自定义类型声明,在 tsconfig.json 的 `include` 中确保覆盖了声明文件所在目录,或者通过 `typeRoots` 指定。
### 路径别名在编辑器中正常但构建时找不到模块
这是因为 tsconfig.json 的 `paths` 只影响 TypeScript 语言服务,Rspack 的模块解析依赖 `resolve.alias`。需要在 rspack.config.js 中添加与 tsconfig.json 一致的 alias 配置。
### SWC 编译后产物体积比 Webpack 大
SWC 默认可能降级到 ES5。在 `jsc.target` 中指定 `'es2022'` 或更高版本,同时启用 `externalHelpers: true`,可以显著减小产物体积。
### 装饰器语法报错
在 SWC parser 配置中设置 `decorators: true`,同时在 tsconfig.json 中配置 `"experimentalDecorators": true`。如果使用 TC39 Stage 3 装饰器,SWC 需要设置 `decorators: true` 且 `decoratorVersion: "2022-03"`。
Rspack 的 TypeScript 支持核心在于内置 SWC 编译器带来的零配置转译能力,配合 ForkTsCheckerWebpackPlugin 或独立 tsc 执行类型检查,既能获得极快的构建速度,又能保持完整的类型安全保障。对于从 Webpack 迁移的项目,重点关注 `isolatedModules` 配置、SWC target 设置和路径别名映射这三处差异即可。服务端5月27日 16:49
Rspack Source Map 是如何工作的?Source Map 是前端构建工具的关键调试能力:它记录编译产物到源码的映射关系,让浏览器 DevTools 中显示的是你写的原始代码,而非经过打包、转译后的产物。Rspack 在这一块做了什么,和 webpack 有什么差异,各 devtool 选项该怎么选?下面逐步拆解。
## Source Map 的基本原理
Source Map 本质是一个 JSON 文件(或内联 Base64 字符串),核心字段包括:
- **version**:Source Map 规范版本,当前为 3
- **sources**:原始源文件路径列表
- **mappings**:VLQ 编码的映射信息,记录生成代码的行列号到源码行列号的对应关系
- **names**:映射中引用的标识符
浏览器加载编译后的 JS 文件时,如果发现末尾有 `//# sourceMappingURL=xxx.map` 注释,就会请求该 Source Map 文件,借助 mappings 字段将报错堆栈还原到源码位置。
## Rspack Source Map 的工作流程
Rspack 生成 Source Map 经历以下阶段:
1. **模块解析阶段**:每个模块被解析时,Rspack 会记录原始源码的位置信息。如果 Loader(如 babel-loader、swc-loader)本身输出了 Source Map,Rspack 会将其作为输入继续传递。
2. **模块链式转换阶段**:当多个 Loader 依次处理同一个模块时,Source Map 会沿 Loader 链逐层合并。这就是 `module` 后缀的作用——它会将 Loader 产生的 Source Map 纳入最终结果,而不是只保留 Rspack 自身生成的映射。
3. **Chunk 拼接阶段**:多个模块被拼接到同一个 Chunk 中,Rspack 需要调整每个模块的 Source Map 偏移量,确保拼接后的行号仍然能正确映射回源码。
4. **输出阶段**:根据 `devtool` 配置,决定 Source Map 是内联到 JS 文件中(DataUrl)、生成独立 `.map` 文件,还是通过 `eval()` 包裹。
### Rspack 与 webpack 的差异
Rspack 的 Source Map 实现兼容 webpack 的 devtool 配置体系,但底层使用 Rust 实现,生成速度显著更快。需要注意的几个差异点:
- Rspack 的 `devtool` 选项与 webpack 语义一致,但部分组合(如 `@` 前缀的 preload 模式)尚未支持
- Rspack 在 `cheap` 模式下的构建速度优势更明显,因为 Rust 的行级映射处理比 Node.js 快得多
- `SourceMapDevToolPlugin` 和 `EvalSourceMapDevToolPlugin` 已支持,可作为 `devtool` 的细粒度替代
## devtool 选项详解与选型
Rspack 支持的 devtool 值由几个关键词组合而成,理解这些关键词就能掌握全部选项:
| 关键词 | 含义 |
|--------|------|
| `eval` | 用 `eval()` 包裹模块,通过 `//# sourceURL` 关联源文件,不生成真正的 Source Map |
| `source-map` | 生成独立的 `.map` 文件,完整映射 |
| `cheap` | 只生成行级映射,不包含列信息,构建更快 |
| `module` | 包含 Loader 的 Source Map,映射到原始源码(如 JSX/TS)而非转译后的代码 |
| `hidden` | 生成 `.map` 文件但不添加 `//# sourceMappingURL` 注释 |
| `nosources` | Source Map 中不包含源码内容,只有位置映射 |
| `inline` | Source Map 以 DataUrl 内联到 JS 文件中,不生成独立文件 |
### 开发环境推荐
```javascript
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map'
}
```
**为什么选 `eval-cheap-module-source-map`**:
- `eval` 使模块独立执行,增量构建时只重新生成变化的模块,rebuild 速度极快
- `cheap` 跳过列映射,大多数调试场景只需要定位到行
- `module` 确保 Loader 转换前的源码被映射,你在 DevTools 中看到的是 TSX/JSX 而非编译后的 JS
如果需要精确到列的断点调试,可升级为 `eval-source-map`,代价是首次构建稍慢。
### 生产环境推荐
根据目标场景选择:
```javascript
// 场景一:需要完整调试信息(内部工具/不公开部署)
module.exports = {
mode: 'production',
devtool: 'source-map'
}
// 场景二:配合 Sentry 等错误追踪平台,不向用户暴露 Source Map
module.exports = {
mode: 'production',
devtool: 'hidden-source-map'
}
// 场景三:只暴露错误位置,不暴露源码内容
module.exports = {
mode: 'production',
devtool: 'nosources-source-map'
}
```
**安全提醒**:生产环境的 `.map` 文件绝不能部署到公开 CDN。使用 `hidden-source-map` 时,Source Map 文件生成但不被浏览器自动加载,你可以将其上传到错误追踪服务后删除。
## SourceMapDevToolPlugin 细粒度控制
当 `devtool` 的组合选项不满足需求时,可以用 `SourceMapDevToolPlugin` 精确控制:
```javascript
const { SourceMapDevToolPlugin } = require('@rspack/core');
module.exports = {
plugins: [
new SourceMapDevToolPlugin({
// 输出文件名模板
filename: '[file].map',
// 排除 node_modules,减小 Source Map 体积
exclude: [/node_modules/],
// 控制是否追加 sourceMappingURL 注释
append: '\n//# sourceMappingURL=[url]',
// 是否生成列映射,false 等同于 cheap
columns: true
})
]
}
```
关键参数说明:
- **filename**:支持 `[file]`、`[chunkHash]` 等模板变量,设为 `false` 则内联到 JS
- **exclude / include**:按模块路径过滤,避免为第三方库生成 Source Map
- **columns**:设为 `false` 可跳过列映射,效果等同于 `cheap`,显著提升构建速度
- **append**:设为 `false` 则不添加 sourceMappingURL 注释,等同于 `hidden`
## Source Map 与错误追踪集成
### Sentry 集成
```javascript
const SentryWebpackPlugin = require('@sentry/webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'hidden-source-map',
plugins: [
new SentryWebpackPlugin({
authToken: process.env.SENTRY_AUTH_TOKEN,
org: 'your-org',
project: 'your-project',
include: './dist',
// 上传后可在构建流水线中删除 .map 文件
rewrite: true
})
]
}
```
### 本地还原错误堆栈
当没有错误追踪平台时,可以用 `source-map` 库手动还原:
```javascript
const { SourceMapConsumer } = require('source-map');
const fs = require('fs');
async function resolveError(line, column) {
const rawSourceMap = JSON.parse(fs.readFileSync('./dist/main.js.map', 'utf8'));
const consumer = await new SourceMapConsumer(rawSourceMap);
const pos = consumer.originalPositionFor({ line, column });
console.log(`源码位置:${pos.source}:${pos.line}:${pos.column}`);
consumer.destroy();
}
```
## 性能影响与优化策略
Source Map 对构建的影响主要体现在三个维度:
### 构建时间
不同 devtool 的首次构建时间大致排序(从快到慢):
```
eval < eval-cheap-source-map < eval-cheap-module-source-map < eval-source-map < source-map
```
`cheap` 跳过列映射可节省 30%-50% 的 Source Map 生成时间;`eval` 通过缓存模块结果让增量构建几乎瞬间完成。
### 产物体积
独立 `.map` 文件通常比原文件大 2-5 倍,因为包含了完整的源码和映射信息。内联模式下 Source Map 以 Base64 编码直接嵌入 JS,体积膨胀更为明显。
### 内存占用
Rspack 在构建过程中需要将 Source Map 数据保存在内存中。对于超大型项目,`source-map` 模式可能导致内存压力,此时 `cheap` 模式是更务实的选择。
### 优化实践
- 排除 `node_modules`:第三方库的 Source Map 对调试无意义,使用 `SourceMapDevToolPlugin.exclude` 过滤
- 开发环境优先选 `eval` 系列:增量构建速度差异可达 10 倍以上
- 生产环境单独生成:在 CI 中生成 `.map` 文件上传到错误追踪服务后删除,不进入部署产物
- 监控构建内存:如果 `source-map` 模式下内存溢出,降级为 `cheap-source-map`
## 常见问题排查
### Source Map 不生效
检查以下原因:
1. `devtool` 是否配置为 `false` 或 `(none)`
2. Loader 是否正确传递 Source Map(如 `babel-loader` 需要设置 `sourceMaps: true`)
3. 浏览器 DevTools 中是否开启了 Source Map 功能(Settings → Enable JavaScript source maps)
4. `hidden-source-map` 模式下浏览器不会自动加载,这是预期行为
### 行号对不上
通常是因为缺少 `module` 关键词。`cheap-source-map` 映射到 Loader 转译后的代码,而 `cheap-module-source-map` 会追溯到 Loader 之前的原始源码。如果你的代码经过 babel/swc 转译,必须使用 `module` 变体才能得到正确的行号。
### 生产环境 Source Map 泄露
如果 `.map` 文件可以被公开访问,检查:
- 构建产物是否包含了 `.map` 文件
- `sourceMappingURL` 是否被意外包含(应使用 `hidden-source-map`)
- 服务器是否正确配置了 `.map` 文件的访问控制服务端5月27日 16:48
Rspack 插件系统是怎么工作的?Rspack 的插件系统基于 Rust 实现,同时提供与 Webpack 高度兼容的 JavaScript API,是 Rspack 扩展性和灵活性的核心机制。Rspack 的大部分原生功能(模块解析、代码分割、Tree Shaking 等)都通过 Rust 侧的内部插件完成,而用户侧的插件开发接口则对齐了 Webpack 的 Compiler/Compilation 钩子体系。理解这套插件系统的工作原理,是从 Webpack 迁移到 Rspack 以及开发自定义构建能力的关键。
## 插件系统架构
Rspack 的插件系统以钩子(Hook)为核心,开发者通过在构建流程的各个阶段注册回调来注入自定义逻辑。整体架构分为两层:
- **Rust 侧插件**:Rspack 的核心构建逻辑全部通过 Rust 插件实现,包括模块解析、依赖分析、代码生成和产物优化。这些内部插件性能极高,但用户通常不需要直接操作它们。
- **JavaScript 侧插件**:对齐 Webpack 的插件 API,用户通过 `apply(compiler)` 方法注册钩子回调。Rspack 已经兼容了大部分 Webpack 的 Compiler 和 Compilation 钩子。
插件的核心能力包括:
- 修改构建配置(`compiler.options`)
- 拦截模块的构建和解析过程
- 在产物输出前处理和优化资源
- 生成额外的文件(HTML、CSS、Source Map 等)
## 插件分类
### 兼容 Webpack 的内置插件
为了与 Webpack 功能保持一致,Rspack 复制了大部分 Webpack 内置插件,保持相同的命名和配置参数。常用插件包括:
- **HtmlWebpackPlugin**:基于模板生成 HTML 文件,自动注入打包后的 JS/CSS 引用
- **MiniCssExtractPlugin**:将 CSS 从 JS bundle 中提取为独立文件,支持按需加载
- **DefinePlugin**:在编译时替换代码中的全局变量,常用于注入环境变量
- **CopyWebpackPlugin**:将静态资源复制到输出目录
- **CleanWebpackPlugin**:每次构建前清理输出目录
### Rspack 独有的内置插件
Rspack 提供了一些原生高性能插件作为 Webpack 对应插件的替代方案,它们在 Rust 层实现,性能显著优于 JavaScript 实现:
- **RspackHtmlPlugin**:Rust 实现的 HTML 生成插件,构建速度远快于 HtmlWebpackPlugin
- **RspackCssExtractPlugin**:Rust 实现的 CSS 提取插件,避免了 JavaScript 与 Rust 之间的通信开销
这类插件的使用方式和配置项与 Webpack 版本基本一致,迁移成本极低。
### 社区插件和 unplugin 支持
Rspack 支持使用社区提供的 Webpack 兼容插件。可以在 [awesome-rspack](https://github.com/web-infra-dev/awesome-rspack) 中查看已验证的社区插件列表。
此外,Rspack 支持基于 unplugin 实现的插件。unplugin 是一套跨构建工具的插件抽象层,使用时需要引入插件的 `/rspack` 子路径:
```javascript
import Icons from 'unplugin-icons/rspack';
module.exports = {
plugins: [Icons({ compiler: 'vue3' })],
};
```
## 使用插件
### 基本配置
```javascript
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { DefinePlugin } = require('@rspack/core');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
}),
new DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
],
};
```
### CSS 提取配置
```javascript
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css',
}),
],
};
```
### 环境变量注入
```javascript
const { DefinePlugin } = require('@rspack/core');
module.exports = {
plugins: [
new DefinePlugin({
'process.env.API_URL': JSON.stringify('https://api.example.com'),
'process.env.VERSION': JSON.stringify('1.0.0'),
}),
],
};
```
## 开发自定义插件
### 插件基本结构
Rspack 的自定义插件结构与 Webpack 完全一致:一个带有 `apply` 方法的类,`apply` 接收 `compiler` 实例作为参数。以下是一个注册多个阶段钩子的完整示例:
```javascript
class MyCustomPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
// 编译开始时执行
compiler.hooks.run.tapAsync('MyCustomPlugin', (compiler, callback) => {
console.log('Starting compilation...');
callback();
});
// compilation 创建后执行
compiler.hooks.compilation.tap('MyCustomPlugin', (compilation) => {
compilation.hooks.processAssets.tapAsync(
{
name: 'MyCustomPlugin',
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
},
(assets, callback) => {
// 在此处理或修改产物资源
callback();
},
);
});
// 编译完成时执行
compiler.hooks.done.tap('MyCustomPlugin', (stats) => {
console.log('Compilation completed!');
});
}
}
module.exports = MyCustomPlugin;
```
### Compiler 钩子
Compiler 代表整个构建流程的实例,以下是最常用的钩子:
| 钩子 | 类型 | 触发时机 |
| --- | --- | --- |
| `run` | AsyncSeriesHook | 编译开始(单次构建) |
| `watchRun` | AsyncSeriesHook | 监听模式下编译开始 |
| `compile` | SyncHook | 编译参数创建后、创建 compilation 前 |
| `compilation` | SyncHook | compilation 创建后,可访问模块工厂 |
| `emit` | AsyncSeriesHook | 输出资源到目录前,可修改最终产物 |
| `done` | SyncHook | 编译完成,可获取 stats |
| `failed` | SyncHook | 编译失败 |
### Compilation 钩子
Compilation 代表单次编译过程,核心钩子包括:
| 钩子 | 类型 | 触发时机 |
| --- | --- | --- |
| `buildModule` | SyncHook | 单个模块开始构建前 |
| `succeedModule` | SyncHook | 单个模块构建成功后 |
| `processAssets` | AsyncSeriesHook | 处理产物资源(优化、替换、新增) |
| `chunkAsset` | SyncHook | chunk 产物生成后 |
钩子类型(SyncHook、AsyncSeriesHook、AsyncParallelHook 等)决定了回调的执行方式:同步钩子按注册顺序依次执行,异步钩子支持 Promise 或 callback 形式。理解钩子类型对开发正确的插件至关重要——在 SyncHook 中使用异步操作会导致构建失败。
## 插件兼容性
Rspack 致力于兼容 Webpack 插件生态,但兼容程度因插件而异:
### 完全兼容
基于 Webpack 公开钩子 API 的插件基本都可以直接使用,包括文件生成、资源处理和大部分优化插件。Rspack 官方维护了插件兼容性列表,已通过测试的 Webpack 插件可以直接迁移。
### 部分兼容
以下情况可能需要调整:
- 依赖 Webpack 内部 API(非公开接口)的插件,如直接访问 `compiler._modules` 等私有属性
- 使用了特定 Webpack 版本才有的特性的插件
- 对 Compilation 的数据结构做了假设的插件(Rspack 的内部数据结构与 Webpack 有差异)
### 不兼容
这几类插件目前无法在 Rspack 中使用:
- 依赖 Webpack 的 JavaScript 运行时特性的插件(Rspack 的核心运行在 Rust 中)
- 深度依赖 Webpack 内部数据结构的插件
- 使用了实验性 API 的插件
遇到不兼容的插件时,可以优先查找是否有 Rspack 原生替代方案,或者使用 unplugin 重新实现插件的跨构建工具版本。
## 性能考量
Rspack 的插件系统在性能方面有几个关键点需要注意:
**优先使用 Rust 原生插件**:Rspack 提供的 RspackHtmlPlugin、RspackCssExtractPlugin 等原生插件在 Rust 侧执行,避免了 JavaScript 和 Rust 之间的跨语言通信开销。Rspack 团队已将多个 JavaScript 插件移植到 Rust,构建性能提升显著。
**减少 Rust-JS 通信**:每个 JavaScript 插件的钩子回调都会触发一次 Rust 到 JavaScript 的跨语言调用。如果自定义插件注册了大量高频钩子(如 `buildModule`),通信开销会累积。可以将逻辑合并到较少的钩子回调中,或使用批量处理来降低调用频率。
**异步钩子的合理使用**:在不需要异步操作的场景中使用同步钩子(`tap` 而非 `tapAsync`),可以减少不必要的异步调度开销。
**插件顺序**:某些插件的执行顺序会影响构建结果。`processAssets` 钩子通过 `stage` 参数控制阶段,使用 `PROCESS_ASSETS_STAGE_ADDITIONS`、`PROCESS_ASSETS_STAGE_OPTIMIZATIONS` 等常量确保插件在正确的阶段执行。
## 从 Webpack 迁移插件到 Rspack
将现有 Webpack 插件迁移到 Rspack 时,按以下步骤排查:
1. **检查兼容性列表**:先确认该插件是否已通过 Rspack 兼容性测试
2. **替换为原生替代**:如果有 Rspack 原生替代方案(如 RspackHtmlPlugin 替代 HtmlWebpackPlugin),优先使用原生版本
3. **避免内部 API**:确保插件只使用 Webpack 公开的钩子 API,不依赖私有属性或方法
4. **测试验证**:迁移后在项目中运行完整构建,对比输出产物是否一致
Rspack 在 `compiler` 实例上暴露了 `compiler.webpack` 命名空间,使依赖 `webpack` 模块的插件可以正常运行。例如,`compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS` 可以直接访问到 Compilation 的常量。
对于暂时无法迁移的 Webpack 插件,也可以考虑通过 unplugin 重新实现其核心逻辑,从而同时支持 Webpack 和 Rspack 两个构建工具。服务端5月27日 15:23
Rspack 相比 Webpack 性能到底强在哪?Rspack 是字节跳动基于 Rust 开发的下一代构建工具,目标很明确:在保持和 Webpack 高度兼容的前提下,把构建速度提上去。
## 为什么 Rspack 比 Webpack 快?
核心原因只有一个——Rspack 用 Rust 写的,Webpack 用 JavaScript 写的。这不是"稍微快一点"的差距,而是系统级语言和脚本语言之间本质的性能鸿沟。
具体来说,差距体现在以下几个层面。
## Rust 原生性能
Rust 编译后的机器码直接跑在 CPU 上,不存在 V8 引擎的解释和 JIT 编译开销。拿最基础的模块解析来说,Webpack 需要通过 Node.js 的 fs 模块做文件 I/O,每一次调用都要经过 JavaScript 运行时的调度;Rspack 直接用 Rust 的文件系统 API,路径解析、文件读取都少了中间层,单这一项就能拉开几倍的差距。
另外 Rust 的零成本抽象不是说说而已——你用泛型、trait 这些高级特性写出来的代码,编译之后和手写底层代码性能几乎一样。所以 Rspack 既能保持代码可维护性,又不牺牲运行效率。
## 多核并行构建
这是拉开差距最大的地方。Webpack 的核心构建流程基本是单线程的,虽然 loader 可以开 worker,但模块依赖图(Dependency Graph)的构建和 chunk 生成是串行的。项目一大,CPU 使用率看着很低,构建时间却死活降不下来。
Rspack 从架构层面就把并行设计进去了:
- 模块解析阶段,多线程同时处理不同入口的依赖关系
- Loader 执行阶段,独立模块的转换可以并行跑
- 代码生成阶段,不同 chunk 的产物可以同时输出
字节跳动内部的大型项目(上万模块)实测数据:构建时间从 3 分钟降到 10 秒左右,提升约 18 倍。这个数字不是理论值,是真实业务项目跑出来的。
## 增量构建和持久化缓存
Webpack 5 引入了持久化缓存,这算是补上了重要的一课。但 Rspack 在增量构建上做得更激进:
- 模块级别的变更检测,只重编真正改过的文件及其依赖链
- 缓存粒度更细,不只是缓存到模块级别,部分中间产物也做了缓存
- 持久化缓存开箱即用,不需要像 Webpack 那样手动配置 cache.type
实际开发中,改一个组件后 HMR 的响应时间基本在 100ms 以内,Webpack 在大项目里经常要 1-3 秒。
## 模块解析优化
Rspack 对模块解析做了几件事:
1. **批量文件系统调用**——把分散的 stat/readfile 调用合并成批量操作,减少系统调用的次数
2. **增强路径解析缓存**——同一个 resolve 请求不重复计算,缓存命中率极高
3. **更紧凑的依赖图数据结构**——用 Rust 的 Vec/HashMap 替代 JavaScript 的对象,内存占用和访问速度都更优
这些优化单个看提升不大,但叠加在一起效果显著。大型项目里光模块解析阶段就能快 5-8 倍。
## 代码分割和 Tree Shaking
Rspack 在产物优化上也做了不少工作:
- Tree Shaking 的实现更精确,能识别 Webpack 可能误判的 side effect
- 代码分割策略更灵活,支持更细粒度的 chunk 划分
- 产物体积通常比 Webpack 小 5-15%
这意味着不仅构建快,产出的代码也更精简。
## 内存管理
Rust 没有垃圾回收(GC),内存分配和释放都是确定性的。Webpack 跑在 Node.js 上,项目一大,GC 暂停就成了问题——构建到一半停下来做垃圾回收,几十秒就没了。
Rspack 的内存管理有几个明显优势:
- 无 GC 暂停,构建过程不会出现突然卡顿
- 内存占用比 Webpack 低 30-50%,同样的机器能构建更大的项目
- 构建完成后内存立刻释放,不会像 Node.js 那样存在内存驻留
## 插件兼容和迁移成本
说性能不说迁移成本就是耍流氓。Rspack 的核心设计目标之一就是兼容 Webpack 生态:
- webpack 配置文件可以直接用,改个 import 路径的事
- 常用 loader(babel-loader、css-loader、style-loader 等)原生支持
- 常用插件覆盖率达到 90% 以上
- 字节跳动内部数百个项目已完成迁移,平均迁移时间 1-2 天
Rspack 1.0 正式版发布后,社区反馈的兼容性问题已经很少了。大多数 Webpack 项目改几行配置就能跑起来,构建速度直接提升 5-10 倍。
## 实际性能数据对比
拿一个典型的中大型前端项目(500+ 模块)做对比:
| 指标 | Webpack 5 | Rspack 1.0 | 提升倍数 |
|------|-----------|------------|---------|
| 冷启动 | 45s | 3s | 15x |
| 增量构建 | 2.5s | 0.15s | 17x |
| 生产构建 | 120s | 8s | 15x |
| 内存占用 | 1.8GB | 0.9GB | -50% |
数据来源是 Rspack 官方基准测试和社区实测,具体数字因项目而异,但量级基本在这个范围。
## 什么时候该考虑迁移?
如果你的项目满足以下条件,迁移到 Rspack 基本是稳赚不赔的:
- 项目用 Webpack 5,构建时间超过 30 秒
- 团队不想大改构建配置,只想提升速度
- 项目主要用社区主流 loader 和插件
如果项目重度依赖 Webpack 的内部 API 或自定义插件,建议先跑一遍 Rspack 的兼容性检查再决定。服务端5月27日 15:10
Rspack 的 Tree Shaking 到底在做什么?## 一个打包产物里的"幽灵代码"
你写了一个工具函数库,导出了 50 个函数,项目里只用到了其中 3 个。打包上线后,你打开产物一看——50 个函数全在里面。这不是假设,而是每天都在发生的事。Tree Shaking 就是解决这个问题的核心机制:让构建工具识别并移除那些从未被使用的代码。
Rspack 作为新一代构建工具,它的 Tree Shaking 实现沿用了 Webpack 的基本框架,但在执行细节和扩展能力上有自己的路径。理解它的工作原理,才能在配置和编码层面真正让"摇树"生效。
## Tree Shaking 的根基:ESM 静态分析
Tree Shaking 之所以可行,根本原因在于 ES Modules 的静态结构特性。ESM 在编译阶段就能确定模块的导入导出关系,这和 CommonJS 有着本质区别:
- `import` 和 `export` 只能出现在文件顶层,不能写在 `if` 语句或函数体内
- 导入的模块路径必须是字符串常量,不能是变量或表达式
- `export` 导出的是值的引用,而非值的拷贝
这三条约束意味着,构建工具不需要执行代码,仅通过静态扫描就能画出完整的模块依赖图,标注哪些导出被使用、哪些从未被引用。CommonJS 的 `require()` 可以在任意位置动态调用,模块路径也可以是变量,运行时才能确定依赖关系,这使得静态分析几乎不可能。
```js
// ESM — 编译时可确定依赖,Tree Shaking 可工作
import { formatDate } from './utils';
// CommonJS — 运行时才确定依赖,Tree Shaking 基本失效
const moduleName = condition ? './utilsA' : './utilsB';
const lib = require(moduleName);
```
## Rspack 的三层 Tree Shaking 机制
Rspack 的 Tree Shaking 并非一步到位删除代码,而是分三个层级逐步收紧:
### usedExports:标记并移除未使用的导出
这是最基础的一层。Rspack 在构建模块依赖图时,追踪每个模块的哪些 `export` 被其他模块实际 `import` 了。未被使用的导出会被标记,在代码生成阶段跳过对应的导出属性。
```js
// utils.js
export const add = (a, b) => a + b; // 被 index.js 引用
export const subtract = (a, b) => a - b; // 未被引用
// index.js
import { add } from './utils';
console.log(add(1, 2));
```
开启 `optimization.usedExports` 后,`subtract` 的导出会被移除。但注意,`const subtract = ...` 的声明本身还留在代码中,只是成了死代码——没有引用指向它。这层优化依赖后续的压缩工具来完成最终删除。
### sideEffects:跳过整个无用模块
`usedExports` 处理的是模块内部的细粒度优化,`sideEffects` 则是模块级别的优化。当 Rspack 发现一个模块的所有导出都没有被使用,并且该模块被标记为无副作用时,整个模块(包括它的子依赖树)会被直接跳过,不进入打包流程。
配置方式是在 `package.json` 中声明:
```json
{
"sideEffects": false
}
```
如果你的库中部分文件有副作用(比如 polyfill、全局 CSS 注入),可以用数组精确指定:
```json
{
"sideEffects": ["./src/polyfill.js", "*.css"]
}
```
`sideEffects` 的优化效果远强于 `usedExports`,因为它能跳过整个子树。但前提是标记必须准确——如果一个有副作用的模块被错误地标记为 `"sideEffects": false`,它会被直接丢弃,导致运行时错误。
### DCE:语句级死代码消除
前两层由 Rspack 自身完成标记和初步清理,最终的语句级消除由压缩工具(Terser 或 SWC Minifier)执行。Rspack 在 `processAssets` 阶段调用压缩器,压缩器识别 `/* unused */` 标记和不可达代码,完成删除。
同时,Rspack 在解析阶段通过 `ConstPlugin` 做基本的死代码消除,比如 `if (false) { ... }` 这种编译期可判定的不可达分支。
## Rspack 与 Webpack 的实现差异
当前版本的 Rspack 在 Tree Shaking 机制上与 Webpack 保持了高度一致,两者共享相同的优化策略和生产模式默认配置。但差异正在显现:
**执行性能**:Rspack 用 Rust 重写了模块图构建和依赖分析逻辑,在大规模项目里,标记未使用导出的速度显著快于 Webpack 的 JavaScript 实现。这对于拥有数千模块的 monorepo 场景尤为明显。
**CSS Tree Shaking**:Rspack 通过内置的 `LightningCssMinimizerRspackPlugin` 实现了 CSS Modules 的 Tree Shaking。该插件利用 Rspack 的 Tree Shaking 信息识别 JS 中未使用的 CSS 类名,交由 lightningcss 执行删除:
```js
// index.module.css
.a { color: red; }
.b { color: blue; } // 未在 JS 中引用
// index.js
import styles from './index.module.css';
document.body.className = styles.a;
// 产物中 .b 被移除,只保留 .a
```
Webpack 生态中要实现类似效果,需要借助 `purgecss` 等 PostCSS 插件,配置更复杂,且无法直接复用打包器的依赖分析结果。
**实验性特性**:Rspack v1.4 引入了 `experiments.inlineConst` 等实验性优化,进一步压缩常量内联空间。Rspack 团队也在探索更高效的 Tree Shaking 策略,未来可能偏离 Webpack 的实现路径。
## sideEffects 配置的陷阱
`sideEffects` 是效果最强但也最容易出错的配置。核心原则:**未在数组中列出的文件一律被视为无副作用,会被直接跳过。**
常见问题:
**IIFE 和全局赋值被丢弃**。一个模块如果在导入时修改了全局对象(如 `window.xxx = ...`),但没有 `export`,标记为 `"sideEffects": false` 后这个模块会被整块删除。
**Polyfill 消失**。Polyfill 通常不导出任何东西,它的作用是修改原型链或全局对象。如果 polyfill 文件没有被列入 `sideEffects` 数组,它会在打包时被移除。
**CSS 文件被忽略**。通过 `import './style.css'` 引入的全局样式文件,虽然看似没有导出,但它们会注入 CSS 到页面。必须在 `sideEffects` 数组中包含 `"*.css"`。
```json
{
"sideEffects": ["./src/polyfill.js", "./src/init.js", "*.css"]
}
```
## innerGraph 与跨模块追踪
生产模式下,Rspack 默认开启 `optimization.innerGraph`。这个优化的作用是追踪模块内部变量的使用链路,即使依赖关系跨越多个模块,也能精确判断某个导出是否真正被使用。
```js
// math.js
export const PI = 3.14159;
export const calculateArea = (r) => PI * r * r;
// index.js
import { calculateArea } from './math';
console.log(calculateArea(5));
```
这里 `calculateArea` 内部依赖了 `PI`。如果 `PI` 没有被其他模块直接 `import`,没有 `innerGraph` 时 Rspack 可能无法确定 `PI` 是否可以通过 `calculateArea` 的引用链保留,有可能误标为未使用。开启 `innerGraph` 后,Rspack 会追踪到 `calculateArea` 内部对 `PI` 的引用,确保两者都正确保留。
## /*#__PURE__*/ 注解:手动标记无副作用
对于立即执行函数调用或类实例化,Rspack 默认认为它们可能有副作用,不会移除。你可以用 `/*#__PURE__*/` 注解明确告诉构建工具:这个调用没有副作用,如果返回值未被使用,可以安全删除。
```js
// 没有 PURE 注解 — Rspack 保留这个调用
const emitter = new EventEmitter();
// 有 PURE 注解 — 如果 emitter 未被使用,整个调用会被移除
const emitter = /*#__PURE__*/ new EventEmitter();
```
这个注解在第三方库的源码中很常见。Lodash-es、RxJS 等库在导出函数时大量使用 `/*#__PURE__*/`,确保未使用的工具函数能被 Tree Shaking 清除。
## CommonJS 的兼容处理
Rspack 对 CommonJS 模块的 Tree Shaking 支持有限,这是由 CommonJS 的动态特性决定的。但 Rspack 并非完全放弃:
**CJS 重新导出的部分场景**:如果一个 CommonJS 模块通过 `module.exports = { a, b }` 导出,Rspack 在某些情况下可以分析出 `a` 和 `b` 两个导出,并对未被使用的那个做标记。但 `Object.assign(module.exports, ...)` 或动态属性赋值则无法分析。
**Babel/SWC 转译陷阱**:如果 Babel 或 SWC 将 ESM 语法转译为 CommonJS(`import` 变成 `require`),Tree Shaking 将失效。确保编译器的模块转换被关闭:
```js
// babel.config.js
{
"presets": [["@babel/preset-env", { "modules": false }]]
}
// rspack.config.js — SWC 默认保留 ESM,无需额外配置
```
**混用场景**:项目中 ESM 和 CommonJS 混用时,ESM 导入 CommonJS 模块会触发整模块打包——因为无法静态确定 CJS 模块导出了哪些成员。反之,CJS 引用 ESM 模块时,Rspack 会尝试做默认导出映射,但效果取决于具体的导出结构。
## 验证 Tree Shaking 是否生效
配置了 Tree Shaking 并不意味着它一定在起作用。你需要主动验证:
### 使用 Rspack Analyze 工具
```bash
rspack --analyze
```
这会生成一个可视化的产物分析报告,展示每个模块的大小和包含关系。如果某个应该被移除的模块仍然出现在产物中,说明 Tree Shaking 对该模块未生效。
### 检查 usedExports 标记
在配置中临时启用 `optimization.usedExports: true` 和 `optimization.providedExports: true`,Rspack 会在产物注释中标记未使用的导出:
```js
/* unused harmony export subtract */
const subtract = (a, b) => a - b;
```
如果在产物中看到 `/* unused harmony export */` 注释但对应的代码仍存在,说明标记生效但压缩器未完成清理——检查 `mode` 是否为 `production`,压缩是否开启。
### 对比产物体积
最直接的方式:分别用 `sideEffects: false` 和 `sideEffects: true` 打包,对比产物大小。如果体积没有变化,要么项目本身已经足够精简,要么 `sideEffects` 配置没有正确识别到可移除的模块。
## Tree Shaking 失效的常见原因清单
排查 Tree Shaking 失效时,按以下顺序检查:
1. **模块格式不对** — 使用了 CommonJS 的 `require/module.exports`,Tree Shaking 无法静态分析
2. **Babel/SWC 将 ESM 转译为 CJS** — 检查编译器配置,确保 `modules` 选项为 `false`
3. **sideEffects 未配置或配置错误** — 未在 `package.json` 中声明,或遗漏了有副作用的文件
4. **压缩器未启用** — `mode` 不是 `production`,或手动关闭了压缩
5. **动态导入** — `import()` 是运行时行为,但动态导入的模块内部如果使用了 ESM 的静态导出,子模块仍然可以被 Tree Shaking
6. **对象或类的属性赋值** — `obj.method = function() {}` 这类动态属性扩展无法被静态分析
7. **全局副作用代码** — 修改 `window`、`document`、`Object.prototype` 等全局对象的代码,Rspack 必须保守保留
8. **第三方库未标记 sideEffects** — 很多旧版本的 npm 包没有 `sideEffects` 字段,Rspack 只能假设它们都有副作用
## 写出可 Tree Shaking 的代码
理解原理的最终目的是指导实践。几个可操作的编码原则:
- 始终使用 ESM 的 `import/export` 语法,避免 `require`
- 导出细粒度的命名导出,而非一个巨大的默认导出对象
- 在库的 `package.json` 中准确声明 `sideEffects`
- 对确定无副作用的函数调用加上 `/*#__PURE__*/`
- 按需导入第三方库:`import { debounce } from 'lodash-es'` 而非 `import _ from 'lodash'`
- 将有副作用的代码(polyfill、全局初始化)独立为单独文件,列入 `sideEffects` 数组
Tree Shaking 不是自动生效的魔法,它需要你在模块设计、依赖选择和构建配置上持续配合。Rspack 提供了和 Webpack 一致的分析能力以及更快的执行速度,但判断代码是否有副作用、是否可以被安全移除,仍然需要开发者自己把关。
前端2月21日 15:35
Rspack 有哪些构建优化策略?Rspack 的构建优化是其高性能的核心,通过多种优化策略可以进一步提升构建速度和输出质量。以下是 Rspack 构建优化的详细说明:
## 构建优化策略
### 1. 增量构建
增量构建只重新构建发生变化的模块,大幅提升构建速度:
```javascript
module.exports = {
cache: {
type: 'filesystem',
cacheDirectory: path.resolve(__dirname, '.rspack-cache'),
buildDependencies: {
config: [__filename]
}
}
}
```
**优化要点**:
- 启用文件系统缓存
- 配置缓存目录
- 指定构建依赖
### 2. 并行处理
Rspack 利用多核 CPU 并行处理构建任务:
```javascript
module.exports = {
parallelism: 4 // 设置并行度
}
```
**优化建议**:
- 根据 CPU 核心数设置并行度
- 避免过度并行导致资源竞争
- 监控系统资源使用情况
### 3. 模块解析优化
优化模块解析路径,减少文件系统访问:
```javascript
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils')
},
symlinks: false,
cacheWithContext: true
}
}
```
**优化要点**:
- 使用别名简化导入路径
- 明确指定扩展名
- 禁用符号链接解析
### 4. 代码分割优化
智能分割代码,优化加载性能:
```javascript
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 20000,
maxSize: 244000,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
automaticNameDelimiter: '~',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
}
```
### 5. Tree Shaking 优化
移除未使用的代码,减少打包体积:
```javascript
module.exports = {
optimization: {
usedExports: true,
sideEffects: true,
providedExports: true
}
}
```
**优化要点**:
- 使用 ES Module 语法
- 正确配置副作用
- 分析打包结果
### 6. 压缩优化
使用高效的压缩工具:
```javascript
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
},
format: {
comments: false
}
},
extractComments: false
})
]
}
}
```
## 性能监控
### 1. 构建时间分析
```javascript
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'bundle-report.html'
})
]
}
```
### 2. 构建性能分析
```javascript
const { StatsWriterPlugin } = require('stats-webpack-plugin');
module.exports = {
plugins: [
new StatsWriterPlugin({
filename: 'stats.json',
stats: {
all: true,
timings: true,
builtAt: true,
assets: true,
chunks: true,
modules: true
}
})
]
}
```
## 内存优化
### 1. 减少内存占用
```javascript
module.exports = {
optimization: {
runtimeChunk: 'single',
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false
}
}
```
### 2. 优化依赖
```javascript
module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
```
## 开发环境优化
### 1. 快速刷新
```javascript
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
hot: true,
client: {
overlay: {
errors: true,
warnings: false
}
}
}
}
```
### 2. 减少构建内容
```javascript
module.exports = {
mode: 'development',
optimization: {
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false
}
}
```
## 生产环境优化
### 1. 完整优化
```javascript
module.exports = {
mode: 'production',
devtool: 'source-map',
optimization: {
minimize: true,
moduleIds: 'deterministic',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
}
```
### 2. 资源优化
```javascript
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8192
}
}
}
]
},
optimization: {
minimizer: [
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
['imagemin-gifsicle', { interlaced: true }],
['imagemin-mozjpeg', { progressive: true }],
['imagemin-pngquant', { quality: [0.65, 0.9] }]
]
}
}
})
]
}
}
```
## 最佳实践
1. **环境区分**:
- 开发环境:快速构建,完整 source map
- 生产环境:完整优化,压缩代码
2. **缓存策略**:
- 使用文件系统缓存
- 配置合理的缓存策略
- 定期清理缓存
3. **性能监控**:
- 定期分析构建性能
- 识别性能瓶颈
- 持续优化
4. **依赖管理**:
- 减少不必要的依赖
- 使用轻量级替代方案
- 优化第三方库使用
5. **代码质量**:
- 编写可优化的代码
- 避免过度优化
- 保持代码可读性
Rspack 的构建优化通过多种策略的组合使用,可以显著提升构建速度和输出质量,为开发者提供更高效的开发体验。前端2月21日 15:35
Rspack 如何处理静态资源?Rspack 的资源处理能力是其构建功能的重要组成部分,能够高效处理各种类型的静态资源。以下是 Rspack 资源处理的详细说明:
## 资源类型
Rspack 可以处理多种类型的资源:
1. **图片资源**:PNG、JPG、GIF、SVG、WebP 等
2. **字体资源**:WOFF、WOFF2、TTF、EOT 等
3. **媒体资源**:MP4、WebM、OGG 等
4. **数据资源**:JSON、XML、CSV 等
5. **其他资源**:TXT、MD 等
## 资源模块类型
Rspack 提供了四种资源模块类型:
### 1. asset/resource
将资源作为单独的文件发出,并导出 URL:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/resource',
generator: {
filename: 'images/[hash][ext][query]'
}
}
]
}
}
```
### 2. asset/inline
将资源作为 data URI 内联到 bundle 中:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset/inline'
}
]
}
}
```
### 3. asset/source
将资源作为源代码导出:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.txt$/i,
type: 'asset/source'
}
]
}
}
```
### 4. asset
自动选择 resource 或 inline:
```javascript
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8192 // 小于 8KB 的文件内联
}
}
}
]
}
}
```
## 图片处理
### 基本配置
```javascript
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/i,
type: 'asset',
generator: {
filename: 'images/[name].[hash:6][ext]'
}
}
]
}
}
```
### SVG 处理
```javascript
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
oneOf: [
{
resourceQuery: /component/,
use: ['@svgr/webpack']
},
{
type: 'asset/resource'
}
]
}
]
}
}
```
### 图片优化
```javascript
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8192
}
}
}
]
},
optimization: {
minimizer: [
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
['imagemin-gifsicle', { interlaced: true }],
['imagemin-mozjpeg', { progressive: true }],
['imagemin-pngquant', { quality: [0.65, 0.9] }],
['imagemin-svgo', { plugins: [{ removeViewBox: false }] }]
]
}
}
})
]
}
}
```
## 字体处理
### 基本配置
```javascript
module.exports = {
module: {
rules: [
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[hash:6][ext]'
}
}
]
}
}
```
### 字体优化
```javascript
module.exports = {
module: {
rules: [
{
test: /\.(woff|woff2)$/i,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[hash:6][ext]'
},
use: [
{
loader: 'fontmin-webpack',
options: {
glyphs: ['\ue000-\uefff']
}
}
]
}
]
}
}
```
## 媒体资源处理
### 视频处理
```javascript
module.exports = {
module: {
rules: [
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/i,
type: 'asset/resource',
generator: {
filename: 'media/[name].[hash:6][ext]'
}
}
]
}
}
```
## 数据资源处理
### JSON 处理
```javascript
module.exports = {
module: {
rules: [
{
test: /\.json$/i,
type: 'json'
}
]
}
}
```
### XML 处理
```javascript
module.exports = {
module: {
rules: [
{
test: /\.xml$/i,
use: 'xml-loader'
}
]
}
}
```
## 资源加载方式
### 1. ES Module 导入
```javascript
import logo from './logo.png';
import data from './data.json';
console.log(logo); // 资源 URL
console.log(data); // JSON 数据
```
### 2. CommonJS 导入
```javascript
const logo = require('./logo.png');
const data = require('./data.json');
console.log(logo); // 资源 URL
console.log(data); // JSON 数据
```
### 3. 动态导入
```javascript
const loadImage = async () => {
const logo = await import('./logo.png');
return logo.default;
};
```
## 资源命名策略
### Hash 命名
```javascript
module.exports = {
output: {
filename: '[name].[contenthash:8].js',
assetModuleFilename: 'assets/[name].[hash:8][ext]'
}
}
```
### 命名占位符
- `[name]`:资源名称
- `[ext]`:资源扩展名
- `[hash]`:资源 hash
- `[contenthash]`:内容 hash
- `[hash:n]`:指定长度的 hash
## 资源缓存策略
### 长期缓存
```javascript
module.exports = {
output: {
filename: '[name].[contenthash:8].js',
assetModuleFilename: 'assets/[name].[contenthash:8][ext]'
},
optimization: {
runtimeChunk: 'single',
moduleIds: 'deterministic'
}
}
```
### CDN 配置
```javascript
module.exports = {
output: {
publicPath: 'https://cdn.example.com/assets/'
}
}
```
## 资源压缩
### Gzip 压缩
```javascript
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
]
}
```
### Brotli 压缩
```javascript
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new CompressionPlugin({
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
]
}
```
## 最佳实践
1. **资源优化**:
- 压缩图片和字体
- 使用合适的格式
- 按需加载资源
2. **缓存策略**:
- 使用 contenthash
- 配置长期缓存
- 利用 CDN 加速
3. **性能优化**:
- 内联小资源
- 懒加载图片
- 使用 WebP 格式
4. **开发体验**:
- 合理配置资源路径
- 提供清晰的命名
- 优化构建速度
Rspack 的资源处理功能为开发者提供了强大而灵活的资源管理能力,通过合理配置和优化,可以显著提升应用性能和用户体验。服务端2月21日 15:35
Rspack 如何支持微前端架构?Rspack 在微前端架构中扮演着重要角色,能够为微前端应用提供高效的构建和部署支持。以下是 Rspack 在微前端中的应用详解:
## 微前端基本概念
微前端是一种将前端应用拆分为多个独立、可独立开发和部署的小型应用的架构模式。每个微前端应用可以:
- 独立开发、测试和部署
- 使用不同的技术栈
- 独立运行和更新
- 组合成完整的应用
## Rspack 在微前端中的优势
1. **快速构建**:
- Rspack 的高性能构建能力特别适合微前端的多应用场景
- 每个微前端应用可以独立构建,构建时间短
- 增量构建进一步提升效率
2. **模块联邦**:
- 支持模块联邦,实现应用间的代码共享
- 动态加载远程模块,减少重复代码
- 提升应用加载性能
3. **独立部署**:
- 每个微前端应用可以独立构建和部署
- 支持不同的构建配置
- 灵活的版本管理
## 模块联邦配置
### Host 应用配置
```javascript
const ModuleFederationPlugin = require('@rspack/core').ModuleFederationPlugin;
module.exports = {
entry: './src/index.js',
mode: 'development',
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js',
app2: 'app2@http://localhost:3002/remoteEntry.js'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
}
```
### Remote 应用配置
```javascript
const ModuleFederationPlugin = require('@rspack/core').ModuleFederationPlugin;
module.exports = {
entry: './src/index.js',
mode: 'development',
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/Button',
'./Header': './src/Header'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
})
]
}
```
## 动态加载远程模块
```javascript
// 在 Host 应用中动态加载远程模块
const loadRemoteModule = async (remoteName, moduleName) => {
const remote = await import(remoteName);
const module = await remote.get(moduleName);
return module;
};
// 使用示例
const loadApp1Button = async () => {
const Button = await loadRemoteModule('app1', './Button');
return Button;
};
```
## 微前端架构模式
### 1. 基座应用模式
```javascript
// 基座应用配置
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
dashboard: 'dashboard@http://localhost:3001/remoteEntry.js',
settings: 'settings@http://localhost:3002/remoteEntry.js',
profile: 'profile@http://localhost:3003/remoteEntry.js'
},
shared: ['react', 'react-dom', 'react-router-dom']
})
]
}
```
### 2. 独立部署模式
每个微前端应用独立部署,通过路由或导航进行切换:
```javascript
// 路由配置
const routes = [
{
path: '/dashboard',
component: lazy(() => import('dashboard/Dashboard'))
},
{
path: '/settings',
component: lazy(() => import('settings/Settings'))
}
];
```
### 3. 混合模式
结合基座应用和独立部署的优势:
```javascript
// 核心功能在基座应用中
// 业务功能作为独立微前端应用
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'main',
remotes: {
business1: 'business1@http://localhost:3001/remoteEntry.js',
business2: 'business2@http://localhost:3002/remoteEntry.js'
},
shared: {
'react': { singleton: true },
'react-dom': { singleton: true },
'shared-ui': { singleton: true }
}
})
]
}
```
## 性能优化
### 1. 代码共享优化
```javascript
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app',
shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0',
eager: false
},
lodash: {
singleton: false,
requiredVersion: '^4.17.0'
}
}
})
]
}
```
### 2. 预加载策略
```javascript
// 预加载远程模块
const preloadRemote = async (remoteName) => {
await import(remoteName);
};
// 在应用启动时预加载
preloadRemote('app1');
preloadRemote('app2');
```
### 3. 缓存策略
```javascript
module.exports = {
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js'
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
}
}
}
}
}
```
## 最佳实践
1. **依赖管理**:
- 使用共享依赖减少重复加载
- 明确版本要求,避免兼容性问题
- 合理设置 eager 属性
2. **错误处理**:
- 处理远程模块加载失败
- 提供降级方案
- 监控应用状态
3. **性能监控**:
- 监控远程模块加载时间
- 优化加载策略
- 分析应用性能
4. **版本管理**:
- 使用语义化版本
- 支持多版本共存
- 平滑升级策略
5. **开发体验**:
- 本地开发时使用本地模块
- 提供开发工具支持
- 简化调试流程
## 实际应用场景
1. **大型企业应用**:
- 不同团队独立开发不同模块
- 统一的技术栈和构建工具
- 灵活的部署和更新
2. **多租户应用**:
- 不同租户使用不同的微前端应用
- 独立的配置和功能
- 统一的基座应用
3. **渐进式重构**:
- 逐步将旧应用迁移到微前端架构
- 保持业务连续性
- 降低重构风险
Rspack 在微前端架构中的应用为开发者提供了高效、灵活的构建和部署方案,通过模块联邦和独立部署,可以实现真正的微前端架构,提升开发效率和应用性能。前端2月21日 15:35
Rspack 的模块热更新(HMR)是如何工作的?Rspack 的模块热更新(HMR)是其核心功能之一,相比 Webpack 的 HMR 有显著的性能提升。以下是 Rspack HMR 的详细说明:
## HMR 基本原理
模块热更新允许在开发过程中,当模块发生变化时,只更新变化的模块,而不是刷新整个页面。这样可以保持应用状态,提升开发体验。
## Rspack HMR 的优势
1. **极快的更新速度**:
- Rspack 的 HMR 更新速度可以达到毫秒级
- 相比 Webpack,更新速度提升 10-50 倍
- 在大型项目中优势更加明显
2. **智能的模块更新**:
- 只重新编译和传输发生变化的模块
- 智能识别模块依赖关系,最小化更新范围
- 支持细粒度的模块替换
3. **状态保持**:
- 更新过程中保持应用状态
- 避免页面刷新导致的用户体验中断
- 保留表单输入、滚动位置等状态
4. **错误恢复**:
- 更新失败时自动回滚
- 提供友好的错误提示
- 支持手动触发重新加载
## HMR 配置
Rspack 的 HMR 配置非常简单,在开发模式下默认启用:
```javascript
// rspack.config.js
module.exports = {
mode: 'development',
devServer: {
hot: true, // 启用 HMR
// 其他 devServer 配置
}
}
```
## HMR API
Rspack 提供了与 Webpack 兼容的 HMR API:
```javascript
// 在模块中使用 HMR API
if (module.hot) {
module.hot.accept('./dependency.js', function() {
// 当依赖模块更新时执行
console.log('Dependency updated');
});
module.hot.dispose(function() {
// 模块被替换前执行清理
console.log('Module will be replaced');
});
}
```
## 框架集成
Rspack 与主流前端框架的 HMR 集成:
1. **React**:
- 支持 React Fast Refresh
- 保持组件状态的同时更新组件
- 自动处理函数组件和类组件
2. **Vue**:
- 支持 Vue 的 HMR
- 保持组件状态和实例
- 支持单文件组件(SFC)的热更新
3. **其他框架**:
- 通过框架特定的 HMR 插件支持
- 大部分框架都有现成的集成方案
## 性能优化
Rspack 的 HMR 性能优化包括:
1. **增量编译**:
- 只编译变化的模块
- 利用缓存避免重复编译
- 并行处理多个模块
2. **智能更新**:
- 分析模块依赖图,最小化更新范围
- 只传输必要的代码
- 使用 WebSocket 高效传输更新
3. **内存优化**:
- 高效的内存管理
- 避免内存泄漏
- 支持长时间开发会话
## 最佳实践
1. **合理使用 HMR API**:
- 只在需要时使用 HMR API
- 正确处理模块清理逻辑
- 避免在 HMR 回调中执行耗时操作
2. **配置优化**:
- 根据项目规模调整 HMR 配置
- 合理设置超时时间
- 启用必要的 HMR 插件
3. **错误处理**:
- 监听 HMR 错误事件
- 提供友好的错误提示
- 实现自动恢复机制
Rspack 的 HMR 功能为开发者提供了极致的开发体验,特别是在大型项目中,能够显著提升开发效率。前端2月21日 15:35
Rspack 如何实现代码分割?Rspack 的代码分割功能是其优化应用性能的重要特性,能够有效减少初始加载时间,提升用户体验。以下是 Rspack 代码分割的详细说明:
## 代码分割的基本概念
代码分割是指将代码拆分成多个 bundle,按需加载,而不是将所有代码打包成一个大的 bundle。这样可以:
- 减少初始加载的代码量
- 实现按需加载,提升首屏加载速度
- 优化缓存策略,提高资源复用率
## Rspack 代码分割的方式
1. **入口点分割(Entry Points)**:
通过配置多个入口点实现代码分割:
```javascript
module.exports = {
entry: {
main: './src/main.js',
vendor: './src/vendor.js'
}
}
```
2. **动态导入(Dynamic Import)**:
使用 `import()` 语法实现动态导入:
```javascript
// 静态导入
import { add } from './math';
// 动态导入
import('./math').then(module => {
module.add(1, 2);
});
// 异步函数中使用
async function loadModule() {
const { add } = await import('./math');
return add(1, 2);
}
```
3. **SplitChunksPlugin**:
Rspack 内置了代码分割插件,可以智能提取公共代码:
```javascript
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
}
```
## SplitChunksPlugin 配置详解
1. **chunks**:
- `all`:对所有模块进行分割
- `initial`:只对初始加载的模块进行分割
- `async`:只对异步加载的模块进行分割
2. **minSize**:
- 模块的最小大小,小于此值的模块不会被分割
- 默认值为 30000 字节
3. **maxSize**:
- 模块的最大大小,超过此值的模块会被进一步分割
- 用于实现更细粒度的代码分割
4. **minChunks**:
- 模块被引用的最小次数
- 默认值为 1
5. **maxAsyncRequests**:
- 按需加载时的最大并行请求数
- 默认值为 5
6. **maxInitialRequests**:
- 入口点的最大并行请求数
- 默认值为 3
7. **name**:
- 分割后的 chunk 名称
- 可以是字符串或函数
## 框架集成
1. **React**:
```javascript
import React, { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
```
2. **Vue**:
```javascript
const LazyComponent = () => import('./LazyComponent.vue');
new Vue({
components: {
LazyComponent
}
});
```
3. **路由级代码分割**:
```javascript
// React Router
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
// Vue Router
const routes = [
{
path: '/',
component: () => import('./views/Home.vue')
},
{
path: '/about',
component: () => import('./views/About.vue')
}
];
```
## 性能优化建议
1. **合理设置分割策略**:
- 根据项目规模和特点调整分割配置
- 避免过度分割导致过多的 HTTP 请求
- 平衡 bundle 大小和请求数量
2. **预加载关键资源**:
```javascript
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');
import(/* webpackPreload: true */ './path/to/component.js');
```
3. **分析打包结果**:
- 使用 Rspack 的分析工具查看 bundle 大小
- 识别可以进一步优化的模块
- 监控代码分割效果
4. **缓存优化**:
- 为第三方库设置稳定的 chunk 名称
- 利用长期缓存策略
- 减少不必要的重新下载
## 最佳实践
1. **按功能模块分割**:
- 将不同功能模块分割成独立的 chunk
- 便于维护和按需加载
2. **提取公共依赖**:
- 使用 SplitChunksPlugin 提取公共代码
- 减少重复代码
3. **懒加载非关键代码**:
- 对非首屏代码使用动态导入
- 提升首屏加载速度
4. **监控和优化**:
- 定期分析打包结果
- 根据实际使用情况调整分割策略
- 持续优化加载性能
Rspack 的代码分割功能为开发者提供了强大的性能优化工具,通过合理的配置和使用,可以显著提升应用的加载性能和用户体验。前端2月21日 15:35
什么是 Rspack,它与 Webpack 有什么区别?Rspack 是一个基于 Rust 语言开发的高性能前端构建工具,旨在提供比传统 Webpack 更快的构建速度和更好的开发体验。它利用 Rust 的高性能和安全特性,实现了极致的构建性能,同时保持了与 Webpack 生态的兼容性。
Rspack 的核心特点包括:
1. **高性能构建**:使用 Rust 编写,利用 Rust 的零成本抽象和内存安全特性,大幅提升构建速度。相比 Webpack,Rspack 在大型项目中可以实现 10-100 倍的构建速度提升。
2. **Webpack 兼容**:Rspack 设计时充分考虑了与 Webpack 的兼容性,支持大部分 Webpack 的配置和插件,开发者可以无缝迁移现有项目。
3. **模块热更新(HMR)**:提供快速的 HMR 支持,在开发过程中实现毫秒级的热更新,提升开发效率。
4. **代码分割**:支持智能代码分割,自动识别公共依赖,优化打包体积,提升应用加载性能。
5. **Tree Shaking**:实现高效的 Tree Shaking,自动移除未使用的代码,减少最终打包体积。
6. **增量构建**:支持增量构建,只重新构建发生变化的模块,进一步提升构建速度。
7. **TypeScript 支持**:内置 TypeScript 支持,无需额外配置即可处理 TypeScript 文件。
8. **CSS 处理**:提供强大的 CSS 处理能力,支持 CSS Modules、PostCSS 等。
Rspack 的架构设计使其能够充分利用多核 CPU 的优势,通过并行处理构建任务,显著提升构建效率。同时,Rspack 的插件系统设计灵活,开发者可以轻松扩展其功能。
在实际应用中,Rspack 特别适合大型前端项目和需要快速构建的场景,能够显著缩短构建时间,提升开发体验。