swc 比 Babel 快 20 倍,但别无脑冲

之前 GitHub 上有个项目突然蹦到 33k stars,就是 swc。说是什么 Rust 写的 JavaScript 编译器,号称比 Babel 快 20 倍。我这种后端出身的人第一反应是:又来一个玩 Rust 花活儿的?但仔细看了看,发现这东西还真不是闹着玩的——Vercel 已经在 Next.js 里用它做编译了。

今天不吹不黑,从实际项目出发,聊聊 swc 到底能不能用、怎么用、以及什么时候应该忍住别用。

它要解决什么问题

前端构建的痛,每个写过 React 的人都有体会:Babel 编译慢、配置多、装一堆插件,大型项目每次保存要等一两秒才能看到更新。swc(Speedy Web Compiler)想用 Rust 重写整个 JS 转译链路,把编译速度提升几个数量级。

官方基准测试数据(来自 README):

  • 单线程下比 Babel 快 20 倍
  • 四线程下比 Babel 快 70 倍
  • 代码压缩(Terser 替代)快 5-10 倍

注意这些是在简单转换(比如 React JSX -> JS)下的测试,实际项目差异可能受其他因素影响,但数量级差距是实打实的。

核心功能:不只是编译,还能压缩

swc 目前主要做三件事:

  1. JS/TS 编译:ES6+ → ES5,TypeScript → JavaScript
  2. 代码压缩:替代 Terser
  3. 模块打包(实验性):swcpack(还没发布正式版,目前仍推荐 webpack/vite)

最常用的是前两个。来一个最小可运行示例:

bash
1 2 3 4 5
# 安装
npm i -D @swc/core @swc/cli

# 编译单个文件
npx swc input.js -o output.js

.swcrc 配置文件(和 Babel 的 .babelrc 类似):

json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true
    },
    "transform": {
      "react": {
        "runtime": "automatic"
      }
    }
  },
  "module": {
    "type": "es6"
  }
}

把这段配置放到项目根目录,然后 npx swc src --out-dir dist,就能一次性编译整个文件夹。

swc configuration file example with TypeScript and React

如果你用 webpack,可以搭配 swc-loader

js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(js|ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'swc-loader'
        }
      }
    ]
  }
}

这时候 webpack 的编译速度会有明显提升——我自己的项目中,从 Babel 切到 swc,HMR 热更新从 1.2s 降到了 0.3s。

和同类项目比,它强在哪?

先说 esbuild。这俩都是用原生语言写的,但 esbuild 是 Go 写的,swc 是 Rust 写的。功能上两者重叠度很高:esbuild 也能转译 JS/TS,也能压缩,还自带打包器。那为什么还要用 swc?

主要区别在 生态兼容性

  • esbuild 的 API 和插件系统是自成一派的,没法直接用 Babel 插件
  • swc 的设计思路是尽量兼容 Babel 的配置和接口,比如 .swcrc 很多字段参考了 Babel 的 preset 概念

所以如果你的项目原先深度依赖 Babel(比如用 @babel/plugin-transform-runtime 或者自定义插件),迁移到 esbuild 几乎要重写整个构建链,而迁移到 swc 只需要改配置文件——前提是你没有用那些冷门插件。

再说 Babel(JavaScript 写的)。Babel 的优势:插件生态极其丰富,社区活跃 10 年,几乎所有 edge case 都有现方案。swc 的优势:单纯性能快 20 倍以上,不需要单独安装 preset-env 等一堆包。

实测对比(自己项目):一个 200 个 TSX 文件的项目,Babel(含 @babel/preset-env + @babel/preset-react + @babel/preset-typescript)单文件编译平均 45ms/swc 单文件编译 2.1ms,相差约 21 倍。

适用场景与局限

适合用 swc 的场景:

  1. 新项目,没有历史 Babel 配置包袱
  2. 老旧项目但只用了 @babel/preset-env + @babel/preset-react + TypeScript 这三个基础 preset(这是大多数项目的情况)
  3. 对构建速度敏感的 monorepo 或 CI 环境(swc 能显著减少构建时间)
  4. 需要同时压缩代码且想省掉 Terser 的开销

不适合的场景:

  1. 项目用了大量自定义 Babel 插件(比如 babel-plugin-macrosbabel-plugin-styled-components 等)。swc 的插件体系是 Rust 写的,目前只有极少数社区插件,没法直接兼容 Babel 插件。官方虽然提供了 @swc/plugin 的接口,但你要用 Rust 写插件,学习成本高。
  2. 需要非常精细的 AST 操作(比如代码混淆、自研转换器)。这时应该选 Babel,因为它用 JS 写插件门槛低。
  3. 需要打包功能(比如 webpack 的 tree-shaking 优化)。swc 的打包器还没稳定,不要在生产环境用它打包。

(个人观点) 如果你的项目只是用 Create React App 或 Next.js(它已经内置 swc),那根本不用考虑——CRA 和 Next.js 已经帮你选了。但如果你是自定义 webpack 配置,我建议:先花一个下午试试 swc,如果所有转换都能正常运行,就直接切。如果遇到某个 Babel 插件不兼容,退回 Babel 也不丢人。

快速上手步骤

  1. 安装依赖

    bash
    1
    npm i -D @swc/core @swc/cli
  2. **创建 .swcrc**(最简单的配置):

    json
    1 2 3 4 5 6 7 8 9
    {
    "jsc": {
     "parser": {
       "syntax": "ecmascript",
       "jsx": true
     },
     "target": "es2015"
    }
    }
  3. 运行编译

    bash
    1
    npx swc src -d dist
  4. 集成到构建工具

  • webpack:装 swc-loader,替换 babel-loader
  • Rollup:装 @rollup/plugin-swc
  • Vite:vite 默认用 esbuild,但可以加上 @vitejs/plugin-react-swc 来用 swc 替换 Babel
  1. 检查兼容性
    先跑一遍编译,如果有报错基本是某个语法 swc 的 parser 不识别。去 swc 官网 查兼容表,大部分 ES 特性和 TypeScript 4.x 都支持了。

最后说一句:不要因为 Rust 和星数盲目迁移。工具选型的唯一标准是:它在你的项目上跑得稳不稳、快不快。swc 值得你花一小时试试,但别一开始就全量迁移到生产环境。先用一个小模块做实验,满意了再推广。


如果你已经在用 swc,欢迎留言说说你遇到过的坑(比如 CSS-in-JS 的 Babel 插件不兼容的问题),我会整理成后续文章。