SWC性能优化实战与踩坑经验分享
我的写法,亲测靠谱
先说结论,SWC真的香。作为一个前端老油条,我从去年开始把几个项目的构建工具从Babel换成了SWC,性能提升肉眼可见。特别是那种动辄上千个文件的大型项目,原本要等30秒的热更新,现在基本控制在5秒内。
这里分享下我的最佳实践代码:
// swc.config.js
module.exports = {
jsc: {
parser: {
syntax: "ecmascript",
jsx: true,
dynamicImport: true,
privateMethod: true,
},
transform: {
react: {
runtime: "automatic",
pragma: "React.createElement",
pragmaFrag: "React.Fragment",
},
},
target: "es2015",
},
module: {
type: "es6",
},
};
这个配置看起来简单,但背后踩过的坑可不少。最初我把target设置成”es5″,结果发现很多现代语法被错误转译,尤其是Proxy和Promise相关的代码,调试了大半天才发现问题出在这。建议大家直接用”es2015″,兼容性够用了。
另外那个runtime设为”automatic”也是有讲究的。之前用”classic”模式,每个文件都要手动import React,烦死了。改成自动模式后清爽多了,代码体积也小了一点。
这几种错误写法,别再踩坑了
来说说我踩过的一些典型坑,让大家少走弯路。
第一个常见错误是这样写的:
// 错误示例
module.exports = {
jsc: {
parser: {
syntax: "typescript", // 这里容易出错
tsx: true,
},
},
};
乍一看没啥问题,但如果你的项目同时包含JS和TS文件,这种写法就会导致JS文件解析报错。正确的做法是分开配置:
// 正确写法
const isTsFile = /.tsx?$/.test(filename);
return {
jsc: {
parser: {
syntax: isTsFile ? "typescript" : "ecmascript",
[isTsFile ? "tsx" : "jsx"]: true,
},
},
};
`>
<p>还有个特别隐蔽的坑,就是sourceMaps的配置。很多人直接这么写:</p></code></pre>javascript
// 错误写法
module.exports = {
sourceMaps: true, // 看似没问题
};
>
<p>结果打包后的sourcemap完全对不上,调试时定位不到原始代码。后来我发现得这样写才靠谱:</p>
<pre class="pure-highlightjs line-numbers language-javascript"><code class="no-highlight language-javascript">// 正确写法
module.exports = {
sourceMaps: process.env.NODE_ENV === 'development' ? 'inline' : false,
};
>
<p>开发环境用inline模式,方便调试;生产环境干脆关掉,减少包体积。</p>
<h2>实际项目中的坑</h2>
<p>在真实项目中,最容易出问题的就是插件系统。SWC原生插件支持还不够完善,我尝试过自定义一个plugin来处理特定需求,折腾了两天都没搞定。最后还是用rollup-plugin-swc绕过去了:</p></code></pre>javascript
// rollup.config.js
import swc from 'rollup-plugin-swc';
export default {
plugins: [
swc({
include: /.[jt]sx?$/,
exclude: /node_modules/,
minify: true,
jsc: {
target: 'es2015',
},
}),
],
};
>
<p>这里要注意include正则表达式,我最初写的是/.js$/,结果ts文件全都被忽略了。改成/.[jt]sx?$/后才正常。</p>
<p>还有个小细节,minify选项千万别滥用。虽然压缩代码很爽,但如果源码里有用eval或者with的(我知道这不好,但有些遗留代码真有),开启minify会直接报错。建议像下面这样分环境处理:</p>
`javascript
const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
minify: isProduction && {
compress: {
unused: true,
dead_code: true,
},
mangle: {
keep_classnames: true,
},
},
};
`>
其他值得注意的地方
说几个零碎但重要的点:
- 缓存策略:SWC自带缓存功能,但我发现某些情况下反而会导致编译结果不一致。建议在CI环境中禁用缓存,本地开发时再开启。
- TypeScript支持:官方文档说支持得很好,但实际用下来,对一些高级类型推导还是会出问题。我的解决方案是保留tsc来做类型检查,SWC只负责转译。
- 多线程优化:记得加上--threads参数,特别是在CI机器上。单核跑真的太慢了。
对了,还有个容易忽略的问题:SWC对Node版本要求比较高。我之前在一个老项目里用Node 12跑SWC,各种奇怪的报错。升级到Node 16后就正常了。建议至少用Node 14以上的版本。
结尾总结
以上是我使用SWC过程中总结的一些实战经验。总的来说,SWC确实是个不错的工具,性能比Babel强太多了。但也不是完美的,比如插件生态还不够丰富,一些边缘case需要自己想办法解决。
我目前的方案可能不是最优解,但至少在实际项目中跑得很稳定。有更好的实践经验欢迎在评论区交流,特别是插件开发这块,如果有人有好的解决方案,希望能分享一下。

暂无评论