用SWC提升前端构建速度的实战经验分享
先来一发最爽的用法:把 Babel 换成 SWC,直接起飞
上周我接手了个老项目,Webpack + Babel 配置一套下来,本地启动要 40 秒,热更新等得我都快睡着了。我实在忍不了,直接上 SWC —— 改完之后,冷启动 8 秒,热更新基本无感。真不是吹,这速度提升是肉眼可见的。
核心改动就两步:一是换掉 babel-loader,二是加个 @swc/loader。代码贴出来,你照着改就行:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: '@swc/loader',
options: {
jsc: {
parser: {
syntax: 'ecmascript',
jsx: true,
},
transform: {
react: {
runtime: 'automatic', // 自动引入 React(17+)
},
},
},
},
},
},
],
},
};
注意,如果你用的是 React,一定要开 runtime: 'automatic',不然会报 React is not defined。这个我踩过坑,折腾了半天才发现是 JSX 转译没配对。
另外,@swc/loader 要装,别只装 @swc/core。我一开始漏装了 loader,结果 Webpack 报错说找不到模块,其实是包名搞混了。
现在跑不起来?看看你的 .swcrc 配置对不对
SWC 的配置文件叫 .swcrc,长这样:
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"decorators": true
},
"target": "es2016",
"loose": false,
"externalHelpers": false
},
"module": {
"type": "commonjs"
}
}
这里有几个关键点:
- 如果你用 TypeScript,
syntax必须设为typescript,不然解析会出错 target我一般设成es2016,兼容性够用,又不至于输出太多 polyfillmodule.type要和你的打包工具匹配。Webpack 默认支持 commonjs,所以没问题。但如果你用 Vite 或 esbuild,建议改成es6或es2020
还有一点,decorators 如果你用了装饰器语法(比如 MobX),必须显式开启,否则直接报错。这个在 Babel 里可能是默认开的,但 SWC 不认,默认是 false。
和 TypeScript 怎么配合?tsconfig.json 别瞎删
很多人以为上了 SWC 就可以删掉 tsconfig.json,大错特错。SWC 只负责转译,不负责类型检查。你要是把 tsconfig 删了,TS 类型报错都没了,等于裸奔。
正确做法是:保留 tsconfig.json 做类型校验,用 SWC 做编译。两者分工明确,一个查错,一个提速。
如果你用 VS Code,它还是会基于 tsconfig 提示错误,完全不受 SWC 影响,这点放心。
Vite 用户注意:vite-plugin-swc 是救星
最近我帮同事搞 Vite 项目,发现原生 Vite 不走 .swcrc,它自己用 esbuild 预构建,开发模式下根本没机会让 SWC 插手。
解决办法:装 vite-plugin-swc,强制让 SWC 在开发时也生效。
// vite.config.js
import { defineConfig } from 'vite';
import swc from 'vite-plugin-swc';
export default defineConfig({
plugins: [
swc({
module: { type: 'es6' },
jsc: {
parser: { syntax: 'typescript', tsx: true },
target: 'es2021',
},
}),
],
});
这个插件亲测有效,开发服务器启动速度几乎没影响,但生产构建能快不少。注意 module.type 设成 es6,不然 import/export 会被转成 commonjs,ESM 环境下可能出问题。
遇到奇怪的语法报错?很可能是 SWC 不支持
SWC 虽然快,但毕竟不是 Babel,生态没那么全。有些实验性语法它不支持,比如:
- top-level await(部分版本支持,但有坑)
- decorator metadata(TypeScript 特有,SWC 不处理)
- 某些 babel-plugin-xxx 的功能(比如自动按需引入 lodash 函数)
举个例子,我们项目里用了 babel-plugin-import 来按需加载 Ant Design 组件,换成 SWC 后发现样式全没了——因为 SWC 不认识这个插件。
解决办法有两个:
- 换用组件库自带的 ES 导出方式,手动按需引入
- 用
swc-plugin-auto-import这类社区插件(不稳定,慎用)
我的建议是:别太依赖 Babel 插件,尽量用标准语法。如果非要用,提前测试,别等到上线才发现。
Node.js 后端也能用?当然,启动快到飞起
我们有个 Node 服务是用 TS 写的,每次改完要重新编译再重启,tsc + nodemon 组合慢得离谱。后来我上了 @swc-node/register,直接运行时编译,快太多了。
// server.ts
import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello from SWC!');
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
# 安装
npm install @swc/core @swc-node/register --save-dev
# 启动
node -r @swc-node/register server.ts
实测比 ts-node 快 3 倍以上。而且内存占用低,适合部署环境。不过注意,生成环境不要用这种方式,还是应该先构建再运行,避免运行时编译风险。
API 调用示例:fetch 数据也更快?
这个其实和 SWC 没直接关系,但既然提到性能,顺带提一嘴。我们在前端请求中用了 SWC 编译后的代码,bundle 更小,首屏加载更快,自然 fetch 也“显得”更快了。
async function fetchData() {
try {
const res = await fetch('https://jztheme.com/api/data');
const data = await res.json();
return data;
} catch (err) {
console.error('Fetch failed:', err);
}
}
虽然这只是个普通请求,但因为整体 JS 包体积小了 30%,页面更早执行到这段代码,用户感知就是“接口变快了”。
踩坑提醒:这三点一定注意
最后总结三个我亲自踩过的坑,你避开就能省半天时间:
- 别忘了装 @swc/loader:只装 @swc/core 没用,Webpack 找不到 loader
- .swcrc 的 module.type 要和打包器匹配:Webpack 用 commonjs,Vite 用 es6,错了会报
__esModule is not a function之类的错 - TSX 文件要显式开启 tsx: true:不然会当普通 TS 解析,JSX 标签直接报错
还有一个小细节:SWC 对 process.env.NODE_ENV 不做注入,如果你代码里依赖这个变量,记得用 DefinePlugin 或其他方式手动注入,不然生产环境可能出问题。
写在最后:SWC 不是万能,但值得尝试
SWC 确实快,但它不是 Babel 的完全替代品。如果你项目重度依赖 Babel 插件,迁移成本会高。但如果是新项目,或者想优化构建速度,我强烈建议直接上 SWC。
我自己现在的项目基本都换掉了 Babel,除了个别老系统还在过渡。速度快了,心情都好了,每天少等几十秒,一年下来也是几个小时。
这个技巧的拓展用法还有很多,比如结合 Turbopack、Rspack 等新构建工具,后续会继续分享这类博客。

暂无评论