新一代前端構(gòu)建工具匯總
說起前端構(gòu)建,大家一定首先想到 Webpack,確實它是前端構(gòu)建的老大哥了,大而全,什么場景都能滿足,社區(qū)生態(tài)爆炸。但是社區(qū)里也有許多其他優(yōu)秀的構(gòu)建工具,他們或許不如 Webpack 那樣“包治百病”,但他們都有一些獨特的優(yōu)勢,如果在一些特定的場景你覺得使用 Webpack 太臃腫了,那你或許可以考慮下面的一些工具。
Parcel
一個號稱「「0 配置」」的打包工具,開箱即用,同時默認使用 Worker 進程充分發(fā)揮多核 cpu 優(yōu)勢來提升構(gòu)建速度,因此在打包效率上還是不錯的,而且 Parcel 2.0 在 SWC 基礎(chǔ)上用 Rust 改寫了 JS/CSS Transformer,進一步提升了構(gòu)建效率。
Parcel 代碼實現(xiàn)得非?!改K化」,有非常多內(nèi)置的插件來完成各種各樣的工作,用戶可以針對自己的需求來使用不同的內(nèi)置插件,只要在 .parcelrc 文件里配置即可,parcel 會自動讀取這個配置文件,不過要注意 .parcelrc 是 JSON5 格式的文件。
文件類型
與 Webpack 不同的是,在 Parcel 中,所有文件都是一等公民,一視同仁,因此不需要用戶去針對不同類型的文件配置各種 Loader,Parcel 會幫你做好不同類型文件的處理。
- 支持 JS/TS/JSX/TSX,Parcel 2.0 開始使用了 Rust 實現(xiàn)的 JS Transformer,能更高效地進行轉(zhuǎn)譯,同時也支持轉(zhuǎn)譯到 ES5,對于 React17 新的 JSX 也能支持。另外 Minification,Tree Shaking 等也是支持的。
- 支持 CSS,功能基本上對齊 CSS Loader,還支持各種 CSS 預(yù)處理語言,支持 Tree Shaking,Minification 等。另外支持以文本形式引入 CSS 資源,方便用戶手動將 css 放入 Style Tag 中,值得一提的是,Parcel2.0 還用 Rust 實現(xiàn)了 CSS 的 Transformer。
- 支持 HTML。
- 支持 Vue,完全支持 Vue3 語法。
- 支持圖片,豐富的圖片文件處理,支持圖片類型的轉(zhuǎn)換以及裁剪。
- ...
構(gòu)建特性
支持 Code Splitting,不過和 esbuild 一樣只能支持比較有限的分割邏輯,被多個入口引用的共用模塊或者使用 import() 動態(tài)引入的模塊會被分割成單獨的 Chunk。
- 支持 Tree Shaking。
- 支持 Scope Hoist。
- 支持 Minification。
- 支持 Compression,可生成 Gzip 和 Brotli 兩種壓縮格式的產(chǎn)物。
- 支持內(nèi)聯(lián) Bundle,即可以以文本或者其他格式引入轉(zhuǎn)譯后的資源,例如上面提到的以文本格式引入編譯后的 CSS 文件,亦可以直接以 dataURL 的格式引入二進制文件等。
- 支持開發(fā)階段的 DevServer,HMR 等。
- 支持瀏覽器緩存,產(chǎn)物文件名默認帶上文件內(nèi)容 hash。
- 支持差異化構(gòu)建,默認會同時構(gòu)建出 ESM 的產(chǎn)物以及非 ESM 的產(chǎn)物。
- ...
優(yōu)點
零配置,告別繁瑣的工程化配置,能夠滿足大多數(shù)場景。在 JS 和 CSS 的轉(zhuǎn)譯上使用了 Rust ,效率上會有所提升。
缺點
擴展性不強,幾乎沒有類似 Webpack 的那種開放性插件特性,因此如果遇到 Parcel 現(xiàn)階段無法實現(xiàn)或有 Bug 的東西,用戶無能為力,只能等 Parcel 去補齊。
使用對比
打包 React + Threejs 項目,Webpack:
Parcel 首次構(gòu)建:
Parcel 非首次構(gòu)建:
Parcel 每次構(gòu)建完都會生成 .parcel-cache 文件記錄各種模塊的依賴關(guān)系,可以大大節(jié)省后續(xù)構(gòu)建的用時,不過這個緩存能力在 Webpack 5.0 也內(nèi)置了,不算是什么獨特的能力。
在產(chǎn)物體積上,雙方大致打平。
總結(jié)
目前 Parcel 最大的賣點就在于無需配置,使用體驗也確實不錯,性能方面在使用 Rust 改造后相信未來也能得到更大的提升,開箱即用可以滿足許多場景,但是封裝性好帶來的副作用就是擴展性差,因此對于有大量定制化構(gòu)建需求的大型項目來說 Parcel 現(xiàn)階段或許不算是一個很好的選擇。
Rollup
Rollup 是當前流行的庫打包器,它比 Webpack 晚幾年出現(xiàn),也是在 ESM 之后出現(xiàn)的,主打的特點是能夠支持并且提倡開發(fā)者使用 ESM 模塊語法進行開發(fā)。
文件類型
幾乎只支持 JS,其他類型的文件均需要使用插件來處理。
特點
Rollup 推崇 ESM 模塊標準開發(fā),這個特點也是借助瀏覽器對 ESM 的支持,Rollup 打包的產(chǎn)物對比 Webpack 會干凈很多。例如同一個項目打包產(chǎn)物:
Webpack 產(chǎn)物:
Rollup 產(chǎn)物:
可以看到 Webpack 產(chǎn)物里是有大量的諸如 __webpack_require__之類的代碼,這些都是 Webpack 自身 Polyfill 的在運行時的模塊加載,就是為了讓產(chǎn)物代碼在所有瀏覽器都能運行,因為 wepack 出現(xiàn)的時候還沒有 ESM ,當時的模塊標準還很混亂,Webpack 抹平了差異。用 IIFE 實現(xiàn)模塊之間的隔離,并且用__webpack_require__ __webpack_exports__ 等 Polyfill 實現(xiàn)在瀏覽器環(huán)境里模擬 CJS 模塊加載,所以我們用 Webpack 打包后的代碼實際上更像是跑在 Webpack 給我們實現(xiàn)的“虛擬 Runtime”上。
而 Rollup 誕生在 ESM 模塊標準出來之后,所以 Rollup 完全遵從 ESM 標準,也就不需要像 Webpack 那樣做很多 Runtime Polyfill,完全把代碼交給瀏覽器運行。對于一些項目里依賴的老舊的 CJS 的包,也可以通過插件來對這些依賴處理。
「Rollup」 「精簡的產(chǎn)物在體積上也是要比」 「webpack」 「來的小?!?/p>
另外對于多入口打包或動態(tài)引入的包也會做分包,我們可以直接使用 [output.manualChunks](https://rollupjs.org/guide/en/#outputmanualchunks) 來自定義分包。
插件系統(tǒng)
Rollup 提供了從 讀取參數(shù) 到 構(gòu)建 到 輸出產(chǎn)物共計 25 種 Hook,足以滿足絕大多數(shù)場景,而且目前社區(qū)里的插件數(shù)量也非常多,幾乎該有的都有了,因為 Rollup 本身只認識 Javascript,所以實際使用過程中我們會需要配置比較多的插件來滿足我們的場景,尤其是項目文件類型比較多樣的情況下。
總結(jié)
Rollup 總體而言是非常優(yōu)秀的打包工具,產(chǎn)物精簡,符合 ESM 標準,豐富的插件系統(tǒng),社區(qū)生態(tài)也很不錯,是個很現(xiàn)代化 Web Bundler。不過相應(yīng)的,他需要支持 ESM 標準的瀏覽器,因此對于低版本瀏覽器也實在沒辦法(愿天堂沒有低版本瀏覽器??)。
因此對于打包 Web App,使用 Webpack 還是主流,干啥都行,哪兒都能跑。
打包庫,推薦使用 Rollup,反正產(chǎn)物最終也是當成依賴引入,瀏覽器兼容性的事情交給引入方去解決了。
Snowpack
Snowpack 主打的是 Unbundle,極速的開發(fā)體驗,在生產(chǎn)環(huán)境也同樣能依賴 Rollup 打包出產(chǎn)物。
他主要的做法就是利用了瀏覽器對 ESModule 的支持,而對于項目用到的依賴,為了防止依賴沒采用 ESM 模塊規(guī)范,Snowpack 會把從依賴入口開始把依賴打包成一個文件,并確保產(chǎn)物是符合 ESM 標準且可以運行在瀏覽器中的,而這里主要是依賴了 esinstall 庫,esinstall 又是通過 rollup 來做這個事情的。
文件類型
- JavaScript (.js, .mjs)。
- TypeScript (.ts, .tsx)。
- JSON (.json)。
- JSX (.jsx, .tsx),默認使用 ESBuild 來轉(zhuǎn)譯,雖然 ESBuild 已經(jīng)有辦法處理新的 JSX 語法了,但 snowpack 似乎沒有兼容上,需要降級到 babel 來處理。
- CSS (.css):對于預(yù)處理語言似乎僅支持 Sass,對于代碼里 import 進來的 css 文件,snowpack 會把它處理成 .proxy.js 后綴的 js 文件,且在 js 文件里的邏輯就是創(chuàng)建 style 標簽把 css 內(nèi)容填進去。
- CSS Modules (.module.css)。
- Images & Assets (.svg, .jpg, .png, etc.)。
- WASM (.wasm)。
插件系統(tǒng)
Snowpack 的插件系統(tǒng)也是利用 snowpack 運行的生命周期中提供的 hooks。且這套是沿襲了 Rollup 的那套插件系統(tǒng)。
- load: 這個 hook 會在加載特定后綴文件的時候觸發(fā),通常用于將瀏覽器無法處理的文件類型轉(zhuǎn)化成瀏覽器能運行的文件,除了可以更改文件內(nèi)容外,也可以更改最終輸出的文件類型。例如插件@snowpack/plugin-vue 對 .vue 文件的處理就是使用這個 hook 來做的。
- transform: 在所有文件都過完 load 之后,會來到 transform hook,這里可以對文件內(nèi)容進行更改。
- optimize:snowpack 本身是不做打包的,但前面說到它也可以支持生產(chǎn)環(huán)境的打包,這里就是依賴插件來做的打包,而插件則是利用的 optimize 這個 hook,在這個 hook 里可以用戶指定打包工具例如 webpack, rollup, parcel 等進行代碼的 bundle。
優(yōu)點
Unbundle 可以提供很快速的開發(fā)體驗,另外插件接口設(shè)計不錯,開發(fā)者可以借此擴展許多應(yīng)用場景。
缺點
官方文檔不是特別的完善,對于一些配置項沒有很清楚的解釋,而且項目維護者沒什么精力去維護這個項目,導致 Snowpack 發(fā)展比較緩慢。
另外插件部分也有一些不足,主要表現(xiàn)為社區(qū)活躍度不夠,生態(tài)不是很完善,可能缺少處理某些場景的插件,甚至一些現(xiàn)存的插件在實現(xiàn)上也不是很完善。使用體驗不夠好。
總結(jié)
由于是采用 Unbundle 的,Snowpack 本身做的東西就不如 Bundle 方案的那些工具多,實際上它主要要做的事情就是幫我們處理好項目依賴,讓那些項目依賴能跑在瀏覽器上就行了。因此它也比較輕量,但還是上面說到的未來發(fā)展的問題,目前更新緩慢,未來會不會繼續(xù)維護也成問題。
Esbuild
它是 Figma 的 CTO 主導,使用 Go 語言編寫的打包工具,熟悉 Vite 的同學對它應(yīng)該不陌生,Vite 中使用 esbuild 做了許多事情,例如轉(zhuǎn)譯 JSX, TS, TSX;預(yù)編譯模塊等。
esbuild 提供兩類 API:Transform 和 Build。
Transform
- 支持轉(zhuǎn)譯的內(nèi)容類型有:JS、JSX、TS、TSX、JSON、CSS、二進制、Text、Base64,不同類型的內(nèi)容需要使用不同的 loader (這里指 esbuild 內(nèi)置的 loader)。
- 支持壓縮。
- 支持 SourceMap。
- 支持指定 Target:轉(zhuǎn)譯成 js 或 css 時可指定目標語法版本,默認 esnext,即使用最新的特性。
- 支持 Tree shaking:主要針對 declaration-level。
Build
Build 實際上是包含了 Transform 過程的,因此在 Transform 中可以配置的字段都能在 Build 中配置。
- 支持 Bundle:默認不啟用 Bundle。
- 支持 Watch:監(jiān)聽文件變動,重新構(gòu)建。
- 支持 DevServer。
- 支持 Code Splitting。
- 支持自定義JS plugin:社區(qū)已經(jīng)有不少 plugin 了 https://github.com/esbuild/community-plugins。
優(yōu)點
不用多說,就是快,壓縮效率也不錯。
缺點
- 沒有提供 AST 級別的 API,用戶無法干涉 Transform 過程,加上 Transform 不能完全支持轉(zhuǎn)譯到 ES5 語法,如果代碼需要運行到低版本瀏覽器或者項目有依賴 Babel Plugin 的話,就不要用 esbuild 了。
- 對 CSS 的支持較為單一,僅支持純 CSS,CSS Modules 在規(guī)劃中了,對于 Less,PostCSS 等預(yù)處理語言則需要用 Plugin 來處理。
- Code Splitting 的功能尚未完善,目前只有當產(chǎn)物是 ESM 的時候才能使用這個特性,而且還有一些 import 順序?qū)е碌膯栴}。
對 TS 的支持也不夠完全,且對 React 17 新的 JSX 處理也還不支持。
雖然有 Plugin 機制,但是提供的鉤子數(shù)量不多,功能也不夠強大,并且 JS Plugin 會在一定程度上拖慢效率。
總結(jié)
目前在業(yè)務(wù)項目里單獨拿 esbuild 做構(gòu)建或者轉(zhuǎn)譯其實都有不少場景是無法支持到的,不過 esbuild 也在不斷完善,我們需要揚之長避之短,現(xiàn)階段在 library 打包場景還是可以用上 ESBuild 的,或者業(yè)務(wù)項目里如果沒有依賴太多的 Babel 插件的情況下倒是可以利用一下 esbuild 的 Transform 能力,比如像 Vite 那樣。
目前前端社區(qū)也有使用 esbuild 結(jié)合 Webpack 的實踐,也正是使用 esbuild 的 Transform 能力作為JS/TS/JSX/TSX 的 loader https://github.com/privatenumber/esbuild-loader。
SWC
全稱 Speedy Web Complier,實際上它并不是構(gòu)建工具,它是基于 Rust 實現(xiàn)的 Complier 工具,但是似乎也有做 Bundle 的規(guī)劃,這里順帶一起介紹了。
得益于 Rust 語言的高效,SWC 的 transform 效率最高可以是 Babel 的 70 倍(官網(wǎng)說的)
SWC is 「20x faster than Babel」 on a single thread and 「70x faster」 on four cores.
SWC官方給出以下幾種包:
- @swc/cli:swc 的命令行工具,可以通過命令行直接對文件進行轉(zhuǎn)譯。
- @swc/core:swc 的 js 庫,可以在 node 環(huán)境中執(zhí)行。
- @swc/wasm-web:swc 的 wasm 版,可以在瀏覽器環(huán)境中執(zhí)行。
- @swc/jest:服務(wù) Jest 框架。
能力一覽
- 支持轉(zhuǎn)譯 JavaScript、TypeScript、J(T)SX、值得注意的是,它還支持轉(zhuǎn)譯 React 17 版本的新 JSX,也能支持「轉(zhuǎn)譯到 ES5 語法」。
- 支持 ESM 或 CJS 等各種模塊標準。
- 支持 Minification。
- 支持 SourceMap。
- 支持插件。
- ...
SWC 也有自己的插件系統(tǒng),并且同時「開放了」 「Rust」 「側(cè)和」 「JS」 「側(cè)的」 「AST」 「級別的」 「API」,所以目前來說 Rust 實際上可以做到任何 Babel 能做的事情。但是目前用戶量還不夠大,可能會存在一些 bug,生態(tài)也還不夠完善。
但是從它開放了 Rust 側(cè)的 API 這點來說還是很誘人的,使用 Rust 開發(fā)的插件在運行效率上比 JS 必然會高出不少。
比起 ESBuild, SWC 是更細粒度的一個工具,可定制化程度也更大,因此目前市面上許多工具譬如 Next.js、Parcel、Deno 都選擇基于 SWC 來做代碼的轉(zhuǎn)譯。
優(yōu)點
除了快以外,關(guān)鍵 SWC 還開放了 Rust 側(cè) AST 級別的 API,在考慮拓展性的同時還把轉(zhuǎn)譯效率上限提高了,可謂是殺手锏了。
缺點
- 目前用戶量還不夠大,深入開發(fā)使用的時候難免踩坑。
- 生態(tài)不夠完善,短期內(nèi)想要替代 Babel 還有些困難。
- Rust 學習困難。
- ...
總結(jié)
作為 Transformer,SWC 的潛力很大,難怪眾多工具都押寶 SWC。但是目前來說 SWC 還處于比較早期,會有一些坑要踩,并且如果單純使用 JS 來開發(fā)插件會是轉(zhuǎn)譯效率大打折扣,因為涉及到不同語言之間 AST 的轉(zhuǎn)換,具體可以看這里 https://github.com/swc-project/swc/issues/2175,因此要發(fā)揮最好的效果勢必要學習 Rust,這個學習曲線可能比較陡峭。
另外 SWC 也提供了 swc-loader 用作 Webpack 的 loader,有興趣可以嘗試一下。
Vite
最后簡要介紹一下 Vite,許多人對他也不陌生了。與 snowpack 類似,他開發(fā)階段采用 unbundle 模式,并且使用 esbuild 做依賴預(yù)構(gòu)建(snowpack 是用的 rollup),生產(chǎn)階段利用 rollup 做構(gòu)建。至于跟 snowpack 的區(qū)別和優(yōu)劣,官網(wǎng)也有介紹,這里就不贅述了。https://cn.vitejs.dev/guide/comparisons.html#snowpack
但毋庸置疑的是,Vite 比 snowpack 更成熟,未來發(fā)展趨勢也更好,Vite 目前可以穩(wěn)定用于生產(chǎn)環(huán)境的。前面我們說到 rollup 會需要高版本瀏覽器支持,那使用 rollup 做生產(chǎn)構(gòu)建的 Vite 是不是也會受同樣限制?實際上 Vite 提供了[@vitejs/plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy) 插件來讓產(chǎn)物可以運行在低版本瀏覽器上,保證了它作為成熟可用于生產(chǎn)環(huán)境的工具的穩(wěn)定性。
為什么不展開介紹,就是因為他已經(jīng)能滿足幾乎場景了,該有的能力都有。當前關(guān)鍵還是看社區(qū)生態(tài),現(xiàn)在 Vite 大大小小的插件也有上百種,未來經(jīng)過更多實際業(yè)務(wù)的考驗想必能跟 webpack 碰一碰。
「參考資料」:
- https://esbuild.github.io/。
- https://swc.rs/docs/getting-started。
- https://jishuin.proginn.com/p/763bfbd6c888。
- https://parceljs.org/blog/rc0/。
- https://juejin.cn/post/7054752322269741064。