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

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

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

配置文件改名那步确实没啥好说的,webpack.config.js 改成 rspack.config.js,webpack 换成 @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-loader 的 syntax: '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-loader 和 cache-loader 直接删掉就行------Rspack 本身就是 Rust 多线程编译,内置持久化缓存,这俩在 Webpack 时代用来加速的 loader 在这反而拖后腿。webpack-dev-server 换成 rspack serve,代理配置大部分兼容,但 historyApiFallback 的 rewrites 函数传参方式有点不一样,踩了一下。 对了还有 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。趁迁移清理一遍,比翻译一份臃肿的配置省事得多。

相关推荐
liangshanbo12151 天前
面试题:Webpack 的 publicPath 有什么作用?
前端·webpack·node.js
DsirNg3 天前
断点不该认识设备:组件布局的容器状态治理
css·前端工程化·组件化·响应式布局·css grid·设计系统·container queries
liangshanbo12153 天前
Webpack vs Vite 底层原理高频追问的问题
前端·webpack·node.js
liangshanbo12153 天前
面试题:AI 对话上下文超限怎么办?
面试·webpack·devops
liangshanbo12154 天前
Webpack 文件指纹面试题整理
前端·webpack·node.js
liangshanbo12154 天前
Webpack 常见 Plugin 面试题
前端·webpack·node.js
liangshanbo12154 天前
Webpack Tree Shaking 面试题整理
前端·webpack·node.js
陪我一起学编程4 天前
vue3笔记
前端·webpack·vue3·vite·脚手架·vue3笔记·组件式
liangshanbo12154 天前
Webpack的分包策略面试题
前端·webpack·node.js
liangshanbo12156 天前
面试题:如何优化 Webpack 的打包速度?
前端·webpack·node.js