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