Webpack迁到Rspack,文档说三步走,实际我折腾了两天

我们组一个React中后台项目,跑了三年多了,452个模块。Webpack 5打包一次生产环境接近两分钟,CI排队的时候看着进度条,总觉得在浪费生命。 上周刷GitHub看到Rspack已经2.1.7了,release note里写了不少性能优化,我寻思趁这周需求间隙试试。官方文档看起来挺清爽的:改个配置文件名,换几个loader,完事。半天就够了吧。 结果折腾了两天。文档写得挺清楚,但坑都在文档没写的地方。

SWC替换Babel,上来就给我一棒子

配置文件改名那步确实没啥好说的,webpack.config.js 改成 rspack.config.jswebpack 换成 @rspack/core,内置插件名字换一换,五分钟搞定。跑 npx rspack build,直接就开始编译了,没报错。 然后我把 babel-loader 换成 builtin:swc-loader,按照文档的示例改了改配置:

css 复制代码
// 改之前
{
  test: /.(j|t)sx?$/,
  loader: 'babel-loader',
  options: {
    presets: [
      '@babel/preset-env',
      '@babel/preset-react',
      '@babel/preset-typescript'
    ],
    plugins: [
      '@babel/plugin-proposal-decorators',
      'babel-plugin-styled-components'
    ]
  }
}

// 改之后(第一版,有问题的)
{
  test: /.(j|t)sx?$/,
  loader: 'builtin:swc-loader',
  options: {
    jsc: {
      parser: {
        syntax: 'typescript',
        tsx: true,
        decorators: true
      },
      transform: {
        react: { runtime: 'automatic' },
        legacyDecorator: true
      }
    }
  }
}

编译炸了。报错大概长这样:

javascript 复制代码
Error: Expected ';', '}' or <EOF>
   ,- src/utils/legacy-parser.ts:42:15
   |
42 |   const result = arr?.filter(x => x != null)
   |                  ^

可选链 ?. 报语法错误??我第一反应是项目里是不是有什么奇怪的语法没注意到,检查了一圈发现不是。然后我以为是 SWC 配置哪里写错了,试了好几种写法,改 parser、改 target、改 transform,折腾了一个多小时都不行。

最后去翻 Rspack 的 issues 才搞明白:builtin:swc-loadersyntax: 'typescript' 并不会自动开启所有 ES 新特性的解析。Babel 的 @babel/preset-env 默认就处理可选链、空值合并这些,但 SWC 不会,得在 jsc 配置里显式指定 target

yaml 复制代码
// 最终能用的版本
{
  test: /.(j|t)sx?$/,
  loader: 'builtin:swc-loader',
  options: {
    jsc: {
      parser: {
        syntax: 'typescript',
        tsx: true,
        decorators: true
      },
      transform: {
        react: { runtime: 'automatic' },
        legacyDecorator: true
      },
      target: 'es2020'  // 加上这行就好了
    }
  }
}

结果还没完。我们项目里有一些 .js 文件也用了 decorators(别问为什么,三年前的代码,什么妖魔鬼怪都有),TypeScript parser 不认 JS 文件里的 decorators。又得给 .js 单独写一条规则:

yaml 复制代码
{
  test: /.js$/,
  loader: 'builtin:swc-loader',
  options: {
    jsc: {
      parser: { syntax: 'ecmascript', decorators: true },
      transform: { legacyDecorator: true }
    }
  }
}

光这一个 loader 就搞了我大半天。Babel 时代 @babel/preset-env 一把梭的日子一去不复返了。SWC 配置粒度更细,但你得真的知道自己在配什么。

另外 babel-plugin-styled-components 这个------SWC 里没有直接对应的插件。我们项目里用 styled-components 不多,趁这个机会直接迁到 CSS Modules 了。如果你们项目里 styled-components 用得重,这个迁移成本得额外算。

CSS Modules全炸了,差点回滚

Loader 搞完之后编译总算过了。心想最难的应该搞定了,跑起来看看效果吧。 页面出来了。然后我差点没认出来------所有样式都乱了。 我们在 Webpack 里 CSS Modules 用的 [local]_[hash:8] 命名:

css 复制代码
// Webpack的配置
{
  loader: 'css-loader',
  options: {
    modules: {
      localIdentName: '[local]_[hash:8]'
    }
  }
}

我一开始在 Rspack 里这么写的:

bash 复制代码
// 有问题的版本
{
  test: /.module.css$/,
  use: [rspack.CssExtractRspackPlugin.loader, 'css-loader'],
  type: 'css/module'
}

问题出在 type: 'css/module' 走的是 Rspack 内置的 CSS Modules 处理,它用的是自己的默认命名规则,完全忽略了 css-loader 里的 localIdentName。两种机制不能混用,我当时不知道,配置写上去跟没写一样。

搞到第二天下午还没搞定的时候,我一度想把配置回滚了,先不迁了。后来静下心来翻 Rspack 文档里 CSS 那章,才发现内置方式要把命名配置放在 module.parser 里:

css 复制代码
// 最终正确的版本
module: {
  parser: {
    'css/module': {
      exports: 'local',
      localIdentName: '[local]_[hash:8]'
    }
  },
  rules: [
    {
      test: /.module.css$/,
      type: 'css/module',
      use: [rspack.CssExtractRspackPlugin.loader]
    }
  ]
}

改完样式恢复了。哦对,还有一点------如果你的项目不是用 .module.css 后缀区分,而是在 webpack 里配 modules: { auto: true } 按文件名自动识别的,Rspack 的 auto 行为和 Webpack 不完全一致,这块得多测试。

剩下的坑不大但都挺烦人

copy-webpack-plugin 换成 rspack.CopyRspackPlugin,配置格式基本一样,这个倒丝滑。fork-ts-checker-webpack-plugin 换成 ts-checker-rspack-plugin,注意 Rspack 2.x 推荐搭配 TypeScript 7 使用,我们还在 TS 5.x,暂时兼容但最好计划升级。 thread-loadercache-loader 直接删掉就行------Rspack 本身就是 Rust 多线程编译,内置持久化缓存,这俩在 Webpack 时代用来加速的 loader 在这反而拖后腿。webpack-dev-server 换成 rspack serve,代理配置大部分兼容,但 historyApiFallbackrewrites 函数传参方式有点不一样,踩了一下。 对了还有 import.meta.env------我一开始看 2.1.6 的 changelog 说支持了,正高兴呢,结果 2.1.7 又给 revert 了。老老实实用 DefinePlugin 吧。

迁移完的感受

冷启动从38秒降到6秒左右,HMR从400多毫秒降到不到100毫秒,生产构建从快两分钟降到15秒上下。内存从将近2G降到600多M。 冷启动那个提升说实话感知不强,等6秒反正也会去倒杯水。但 HMR 是实打实的体感变化------改一行代码页面上几乎是即时反馈,之前那400毫秒的延迟其实每次都在打断思路。CI 构建时间缩短对发布节奏的提升也是真的,push 完基本就能部署了。

值不值得迁,得看你自己的情况。我们项目是 Webpack 5,450多个模块,构建两分钟,迁完确实爽。如果你项目规模差不多,我觉得可以试试。但如果你还在 Webpack 4,先升到5吧,Rspack对4的loader兼容性更差,迁移成本会高不少。已经在用Vite的就别折腾了,Rspack的优势是Webpack存量项目的平滑迁移,跟Vite不在一个赛道。

最后一个建议:迁之前先把你那个 Webpack 配置理一遍。我们那个配置文件三年里好几个人往上叠,里面有不少已经没用的 loader。趁迁移清理一遍,比翻译一份臃肿的配置省事得多。

相关推荐
FungLeo2 小时前
Taro 开发小程序, 把 @ 配成 src 路径别名,webpack / tsconfig / scss 三处同步的正确姿势
webpack·小程序·taro
小洋葱1611 天前
2026 年了,别再用 only-allow 固定包管理器了!devEngines 才是正解
前端工程化
先吃饱再说2 天前
编写一个颜色选择器之后,我理解了 React 工程化的三层架构
react.js·前端框架·前端工程化
先吃饱再说2 天前
从“传事件”到“只传值”:React + TypeScript 组件 Props 设计的两次进化
react.js·前端框架·前端工程化
leslie1182 天前
webpack笔记
前端·webpack
Dr_哈哈4 天前
从一个 Vue 项目到一套工程体系:Bun、Monorepo、Turbo 与 Docker 到底在解决什么?
前端工程化
布兰妮甜4 天前
暗黑模式一键切换完整方案(CSS 变量 + 本地存储)
javascript·css·web开发·用户体验·前端工程化
布兰妮甜4 天前
原子化 CSS 深度解析:Tailwind 原理、自定义配置、大型项目利弊
css·性能优化·tailwind·前端工程化·设计系统
labixiong5 天前
Rspack 2.0 正式发布 — 前端构建工具格局再变天
webpack·前端工程化·turbopack