尤雨溪:Turbopack真的比Vite快10倍嗎?
大家好,我是 CUGGZ。
10 月 25 日,Vercel 推出了下一代打包工具:Turbopack,它是基于 Rust 的 Webpack 繼任者,其文檔中提到,Turbopack 比 Vite 快 10 倍。11 月 1 日,Vue、Vite 作者尤雨溪發(fā)表文章 《Is Turbopack really 10x Faster than Vite?》,對 Turbopack 和 Vite 進(jìn)行了測試對比,下面就來看看詳細(xì)內(nèi)容吧!
在 Turbopack 官方文檔的基準(zhǔn)圖中,最初顯示帶有 Turbopack 的 Next 13 能夠在 0.01 秒內(nèi)執(zhí)行 React Hot-Module Replacement (HMR,即熱更新),而對于 Vite,則需要 0.09 秒。上面也有冷啟動(dòng)性能基準(zhǔn),但由于沒有一個(gè)冷啟動(dòng)基準(zhǔn)顯示出 10 倍的優(yōu)勢,我們只能假設(shè)“快 10 倍”的說法是基于 HMR 性能。
Vercel 并沒有在文檔中包含指向他們用來生成這些結(jié)果的基準(zhǔn)的任何鏈接。所以,尤大決定使用新發(fā)布的 Next 13 和 Vite 3.2 來驗(yàn)證這些聲明。測試的要點(diǎn)是通過測量以下兩個(gè)時(shí)間戳之間的增量來比較 HMR 性能:
- 修改源文件的時(shí)間,通過單獨(dú)的 Node.js 進(jìn)程記錄文件更改;
- 更新的 React 組件重新渲染的時(shí)間,通過Date.now() 調(diào)用直接記錄在組件的渲染輸出中。注意,此調(diào)用發(fā)生在組件的虛擬 DOM 渲染階段,因此它不受 React 協(xié)調(diào)或?qū)嶋H DOM 更新的影響。
該基準(zhǔn)還測量了兩種不同情況下的結(jié)果:
- root:根組件,組件導(dǎo)入 1000 個(gè)不同的子組件并將它們一起渲染。
- leaf:葉子組件,組件由根組件導(dǎo)入,但自己沒有導(dǎo)入組件(沒有子組件)。
細(xì)微差別
在測試這些之前,還有一些額外的細(xì)微差別值得一提:
- Next 是否使用 React 服務(wù)端組件(RSC)。
- Vite 是否使用 SWC 而不是 Babel 進(jìn)行 React 轉(zhuǎn)換。
React 服務(wù)端組件
Next 13 引入了一個(gè)重大的架構(gòu)轉(zhuǎn)變,現(xiàn)在的組件默認(rèn)為服務(wù)端組件,除非用戶使用“use client”指令明確選擇客戶端模式。它不僅是默認(rèn)設(shè)置,Next 文檔還建議用戶盡可能保持服務(wù)端模式,以提高最終用戶的性能。
第一輪測試(Next w/ RSC 和 Vite w/ Babel))
最初的基準(zhǔn)測試是在服務(wù)端模式下使用根組件和葉子組件測量 Next 13 的 HMR 性能。結(jié)果表明,Next 13 在兩種情況下實(shí)際上都較慢,并且對于葉子組件而言差異顯著,測試方法和測試結(jié)果如下。
1、 測試方法
- 這兩個(gè)項(xiàng)目都是通過以下命令新創(chuàng)建的:
- 在每個(gè)項(xiàng)目中運(yùn)行g(shù)enFiles.(m)js 生成 1000 個(gè)組件,所有組件都被導(dǎo)入到應(yīng)用的根組件中并一起渲染。
- 對于每個(gè)項(xiàng)目,在單獨(dú)的 Node 進(jìn)程中運(yùn)行watch.(m)js 以獲取文件更改的確切時(shí)間戳,這用于標(biāo)記 HMR 的開始。
- 啟動(dòng)項(xiàng)目,然后編輯以下文件以測試 HMR:
- Next: app/page.js (根組件) 和 app/comp0.jsx (葉子組件)。
- Vite: src/App.jsx (根組件) 和 src/components/comp0.jsx (葉子組件)。
已編輯的組件都在其輸出中包含 Date.now()。DOM 中最終渲染的時(shí)間戳用于標(biāo)記 HMR 的完成。
2、測試結(jié)果
- 記錄超過 5 次運(yùn)行
- 時(shí)間以毫秒為單位
- 在 M1 Macbook Pro 上測試
第二輪測試(Next w/o RSC 和 Vite w/ Babel))
有人指出,應(yīng)該在沒有 RSC(React 服務(wù)端組件)的情況下對 Next 組件進(jìn)行基準(zhǔn)測試以使其相等。因此,在 Next 根組件中添加了“use client”指令以選擇客戶端模式。事實(shí)上,在客戶端模式下,Next HMR 顯著改進(jìn),比 Vite 快 2 倍,測試方法和測試結(jié)果如下。
1、測試方法
- 這兩個(gè)項(xiàng)目都是通過以下命令新創(chuàng)建的:
- 在每個(gè)項(xiàng)目中運(yùn)行g(shù)enFiles.(m)js 以生成 1000 個(gè)組件。所有組件都被導(dǎo)入到應(yīng)用的根組件中并一起渲染。
- 給 app/page.js 添加 use client 指令,使它以客戶端模式渲染。這是確保正確的比較所必需的,因?yàn)榉?wù)端組件會(huì)產(chǎn)生不小的 HMR 開銷(慢 4 倍)。
- 對于每個(gè)項(xiàng)目,在單獨(dú)的 Node 進(jìn)程中運(yùn)行watch.(m)js 以獲取文件更改的確切時(shí)間戳。這用于標(biāo)記 HMR 的開始。
- 啟動(dòng)項(xiàng)目,然后編輯以下文件以測試 HMR:
- Next: app/page.js (根組件) 和 app/comp0.jsx (葉子組件)。
- Vite: src/App.jsx (根組件) 和 src/components/comp0.jsx (葉子組件)。
2、測試結(jié)果
- 記錄超過 5 次運(yùn)行
- 時(shí)間以毫秒為單位
- 在 M1 Macbook Pro 上測試
SWC 與 Babel 轉(zhuǎn)換
我們的目標(biāo)是讓基準(zhǔn)只關(guān)注 HMR 性能差異,所以需要消除另一個(gè)變量:Vite 的默認(rèn) React 預(yù)設(shè)使用 Babel 來轉(zhuǎn)換 React HMR 和 JSX。
React HMR 和 JSX 轉(zhuǎn)換不是與構(gòu)建工具耦合的特性,它可以通過 Babel(基于 JavaScript)或 SWC(基于 Rust)來完成。Esbuild 也可以轉(zhuǎn)換 JSX,但缺乏對 HMR 的支持。SWC 比 Babel 快得多(單線程時(shí)快 20 倍,多核時(shí)快 70 倍)。Vite 目前默認(rèn)使用 Babel 的原因是安裝大小和實(shí)用性之間的權(quán)衡。SWC 的安裝量相當(dāng)大(node_modules 中有 58 MB,而 Vite 只有 19 MB),并且許多用戶仍然依賴 Babel 進(jìn)行其他轉(zhuǎn)換,因此 Babel 對他們來說是不可避免的。但是,這種情況在未來可能會(huì)發(fā)生改變。
Vite core 并不依賴 Babel,因此使用 SWC 而不是 Babel 來處理 React 轉(zhuǎn)換不需要在 Vite 中進(jìn)行任何更改,只需將默認(rèn)的 React 插件替換為 vite-plugin-swc-react-refresh。切換后可以看到,Vite 在根組件中有顯著提升,趕上了 Next:
有趣的是,這里的增長曲線顯示 Next/turbo 在根組件比葉子組件中慢 4 倍,而 Vite 只慢 2.4 倍。這意味著 Vite HMR 在更大的組件中可伸縮性更好。此外,切換到 SWC 還可以改善 Vite 在 Vercel 基準(zhǔn)測試中的冷啟動(dòng)指標(biāo)。
不同硬件上的性能
由于這是一個(gè)涉及 Node.js 和原生 Rust 部分的復(fù)合基準(zhǔn)測試,因此在不同的硬件上會(huì)有很大差異。以上結(jié)果是在 M1 MacBook Pro 上收集的。其他用戶在不同的硬件上運(yùn)行相同的基準(zhǔn)測試并報(bào)告了不同的結(jié)果。在某些情況下,Vite 在根組件情況下更快,而在其他情況下,Vite 在兩種情況下都明顯更快。
Vercel 的澄清
在尤大發(fā)布了自己的基準(zhǔn)后,Vercel 發(fā)布了一篇博客文章,闡明了他們的基準(zhǔn)方法,并將他們的基準(zhǔn)提供給公眾驗(yàn)證。閱讀文章和基準(zhǔn)代碼后,以下是一些關(guān)鍵點(diǎn):
- Vite 實(shí)現(xiàn)仍然使用默認(rèn)的基于 Babel 的 React 插件。
- 1000 個(gè)組件案例的原始數(shù)字存在舍入問題——Turbopack 的 15ms 被舍入到 0.01s,而 Vite 的 87ms 被舍入到 0.09s。當(dāng)原始數(shù)字接近 6 倍時(shí),這進(jìn)一步被宣傳為 10 倍的優(yōu)勢。
- Vercel 的基準(zhǔn)測試使用更新模塊的“瀏覽器評估時(shí)間”作為結(jié)束時(shí)間戳,而不是 React 組件重新渲染時(shí)間。
- 文章中的圖表顯示,當(dāng)總模塊數(shù)超過 30k 時(shí),Turbopack 可以比 Vite 快 10 倍。
總而言之,如果以下所有條件都成立,“比 Vite 快 10 倍”的說法是成立的:
- Vite 沒有使用相同的 SWC 轉(zhuǎn)換。
- 應(yīng)用包含超過 30k 個(gè)模塊。
- 基準(zhǔn)測試只測量熱更新模塊的評估時(shí)間,而不是實(shí)際應(yīng)用更改的時(shí)間。
什么是“公平”比較?
如果我們要比較的是“開箱即用的默認(rèn)設(shè)置”,那么我們應(yīng)該與 Next 中啟用的 RSC 進(jìn)行比較,因?yàn)檫@是默認(rèn)設(shè)置,也是 Next 積極鼓勵(lì)用戶使用的。由于 Vercel 的基準(zhǔn)測試沒有使用 RSC,并且正在測量“模塊評估時(shí)間”,以排除 React 的 HMR 運(yùn)行時(shí)引起的差異,因此可以公平地假設(shè)基準(zhǔn)測試的目標(biāo)是對 Vite 和 Turbopack 固有的 HMR 機(jī)制進(jìn)行比較。
不幸的是,在這個(gè)前提下,Vite 仍然在基準(zhǔn)測試中使用 Babel 使這個(gè)對比是不平等的。在 Vite 中使用 SWC 進(jìn)行轉(zhuǎn)換更新對比結(jié)果之前,Turbopack 比 Vite 快 10 倍的結(jié)論是不準(zhǔn)確的。
此外,我相信大多數(shù)人都會(huì)同意:
- 對于絕大多數(shù)用戶來說,30k 模塊是極不可能的情況。使用 SWC 的 Vite,達(dá)到 10 倍要求所需的模塊數(shù)量可能會(huì)變得更加不切實(shí)際。雖然理論上是可行的,但用它來營銷 Turbopack 是不誠實(shí)的。
- 與理論上的“模塊評估”時(shí)間相比,用戶更關(guān)心端到端 HMR 性能,即從保存到看到更改的時(shí)間。當(dāng)看到“更新速度快 10 倍”時(shí),普通用戶會(huì)想到前者而不是后者,Vercel 在其營銷中忽略了這一警告。實(shí)際上,Next 中服務(wù)端組件(默認(rèn))的端到端 HMR 比 Vite 中的慢。
作為 Vite 的作者,很高興看到像 Vercel 這樣資金雄厚的公司在改進(jìn)前端工具方面進(jìn)行了大量投資。在適用的情況下,甚至未來可能在 Vite 中利用 Turbopack。然而,開源工具的競爭應(yīng)該建立在公開溝通、公平比較和相互尊重的基礎(chǔ)上,令人失望和擔(dān)憂的是,看到激進(jìn)的營銷使用了精心挑選的、未經(jīng)同行評審的、邊緣誤導(dǎo)性的數(shù)字,這些數(shù)字通常只在商業(yè)競爭中出現(xiàn),相信 Vercel 可以做得更好。
相關(guān)鏈接
- 原文:https://github.com/yyx990803/vite-vs-next-turbo-hmr/discussions/8。
- 代碼:https://github.com/yyx990803/vite-vs-next-turbo-hmr。