新一代的編譯工具 SWC
最近前端圈掀起了一陣 rust 風(fēng),凡是能用 rust 重寫(xiě)的前端工具就用 rust 重寫(xiě),今天介紹的工具就是通過(guò) rust 實(shí)現(xiàn)的 bable:swc,一個(gè)將 ES6 轉(zhuǎn)化為 ES5 的工具。
而且在 swc 的官網(wǎng),很直白說(shuō)自己和 babel 對(duì)標(biāo),swc 和 babel 命令可以相互替換,并且大部分的 babel 插件也已經(jīng)實(shí)現(xiàn)。

使用 rust 的一個(gè)優(yōu)勢(shì)就是快,比如我們之前的一個(gè)項(xiàng)目,將 babel 替換成 swc 后,編譯速度從原來(lái)的 7 秒提升到了 1 秒,效率直接爆炸。

上手
swc 與 babel 一樣,將命令行工具、編譯核心模塊分化為兩個(gè)包。
@swc/cli類(lèi)似于@babel/cli;@swc/core類(lèi)似于@babel/core;
npm?i?-D?@swc/cli?@swc/core
通過(guò)如下命令,可以將一個(gè) ES6 的 JS 文件轉(zhuǎn)化為 ES5。
npx?swc?source.js?-o?dist.js
下面是 source.js 的代碼:
const?start?=?()?=>?{
??console.log('app?started')
}
代碼中囊括了 ES6 的兩個(gè)特性,const 聲明 和 箭頭函數(shù)。經(jīng)過(guò) swc 轉(zhuǎn)化后,這兩個(gè)特性分別被轉(zhuǎn)化成了 var 聲明 和 function 匿名函數(shù)。

配置文件
swc 與 babel 一樣,支持類(lèi)似于 .babelrc 的配置文件:.swcrc,配置的格式為 JSON。
{
??"jsc":?{?//?編譯規(guī)則
????"target":?"es5",?//?輸出js的規(guī)范
????"parser":?{
??????//?除了?ecmascript,還支持?typescript
??????"syntax":?"ecmascript",
??????//?是否解析jsx,對(duì)應(yīng)插件?@babel/plugin-transform-react-jsx
??????"jsx":?false,
??????//?是否支持裝飾器,對(duì)應(yīng)插件?@babel/plugin-syntax-decorators
??????"decorators":?false,
??????//?是否支持動(dòng)態(tài)導(dǎo)入,對(duì)應(yīng)插件?@babel/plugin-syntax-dynamic-import
??????"dynamicImport":?false,
??????//?……
??????//?babel?的大部分插件都能在這里找到對(duì)應(yīng)配置
????},
????"minify":?{},?//?壓縮相關(guān)配置,需要先開(kāi)啟壓縮
??},
??"env":?{?//?編譯結(jié)果相關(guān)配置
????"targets":?{?//?編譯結(jié)果需要適配的瀏覽器
??????"ie":?"11"?//?只兼容到?ie?11
????},
????"corejs":?"3"?//?corejs?的版本
??},
??"minify":?true?//?是否開(kāi)啟壓縮
}
babel 的插件系統(tǒng)被 swc 整合成了 jsc.parser 內(nèi)的配置,基本上大部分插件都能照顧到。而且,swc 還繼承了壓縮的能力,通過(guò) minify 屬性開(kāi)啟,jsc.minify 用于配置壓縮相關(guān)的規(guī)則,更詳細(xì)的配置可查看文檔。
Node APIs
通過(guò)在 node.js 代碼中,導(dǎo)入 @swc/core 模塊,可以在 node.js 中調(diào)用 api 直接進(jìn)行代碼的編譯,這對(duì) CLI 工具的開(kāi)發(fā)來(lái)說(shuō)是常規(guī)操作。
//?swc.mjs
import?{?readFileSync?}?from?'fs'
import?{?transform?}?from?'@swc/core'
const?run?=?async?()?=>?{
??const?code?=?readFileSync('./source.js',?'utf-8')
?const?result?=?await?transform(code,?{
????filename:?"source.js",
??})
??//?輸出編譯后代碼
??console.log(result.code)
}
run()

打包代碼
除了將代碼轉(zhuǎn)義,swc 還提供了一個(gè)簡(jiǎn)易的打包能力。我們新建一個(gè) src 文件夾,在里面新建兩個(gè)文件:index.js、utils.js。
//?src/index.js
import?{?log?}?from?'./utils.js'
const?start?=?()?=>?log('app?started')
start()
//?src/utils.js
export?const?log?=?function?()?{
??console.log(...arguments)
}
export?const?errorLog?=?function?()?{
??console.error(...arguments)
}
可以看到 index.js 導(dǎo)入了 utils.js 中的一個(gè)方法,然后我們新建一個(gè) spack.config.js 文件,該文件是 swc 打包的配置文件。
//?spack.config.js
module.exports?=?{
??entry:?{
????//?打包的入口
????web:?__dirname?+?"/src/index.js",
??},
??output:?{
????//?打包后輸出的文件夾
????path:?__dirname?+?"/dist",
??},
};
然后在命令行運(yùn)行:
$?npx?spack

打包成功后,會(huì)在 dist 目錄輸出一個(gè) web.js 文件。

可以看到,不僅將 index.js、utils.js 打包成了一個(gè)文件,還進(jìn)行了 tree shaking,將 utils.js 中沒(méi)有使用的 errorLog 方法刪掉了。
能不能用?
babel 畢竟經(jīng)過(guò)了這么多年的發(fā)展,不管是 bug 輸了,還是社區(qū)活躍度都遠(yuǎn)遠(yuǎn)優(yōu)于 swc。所以,如果是小產(chǎn)品試水還是可以試一下 swc 的,舊項(xiàng)目如果已經(jīng)使用了 babel 還是不建議進(jìn)行遷移。
在使用的過(guò)程,還是發(fā)現(xiàn)了一些小問(wèn)題。比如,如果我使用了 async function,swc 會(huì)自動(dòng)導(dǎo)入 regenerator-runtime 模塊。
//?編譯前,有個(gè)?async?方法
const?start?=?async?()?=>?{
??console.log('app?started')
}
調(diào)用 swc 編譯后,代碼如下:

這個(gè)結(jié)果看起來(lái)是沒(méi)問(wèn)題的,但是 swc 與 babel 類(lèi)似,也有 helpers(@swc/helpers),同時(shí)提供了 externalHelpers 開(kāi)關(guān), 如果把 externalHelpers 設(shè)置為 true,swc 會(huì)將一些工具類(lèi),通過(guò)模塊的形式導(dǎo)入。
//?.swcrc
{
??"jsc":?{
????"externalHelpers":?true
??}
}

而 externalHelpers 的默認(rèn)值是 false,那這個(gè)時(shí)候,regenerator-runtime ,到底是通過(guò)模塊的形式導(dǎo)入,還是把整個(gè)代碼寫(xiě)入到文件?
swc 正好有個(gè) issue [https://github.com/swc-project/swc/issues/1461] 在討論這個(gè)問(wèn)題。
除了上面說(shuō)的這個(gè)問(wèn)題,其實(shí)還有一點(diǎn),就是作者覺(jué)得之前的架構(gòu)有問(wèn)題,正在加緊重寫(xiě) 2.0 版本,感覺(jué)可以期待一下,另外提一句,swc 的作者是一個(gè) 97 年的韓國(guó)小哥,目前大學(xué)都還沒(méi)畢業(yè),最后我也只能說(shuō)一句:牛逼!
- END -