爲什麼有人說 vite 快,有人卻說 vite 慢?
前言
談到 Vite
,給人的第一印象就是 dev server
啓動速度快。同樣規模的項目,相比 Webpack
動輒十幾秒甚至幾十秒的的啓動速度,Vite
簡直是快到沒朋友,往往數秒之內即可完成啓動 (PS: 都沒有時間去喝一杯 ☕️ 啦)。
正好最近在做一些關於開發體驗的性能優化,就想着把手上一些項目的開發模式更新爲 Vite
。經過一番操作,終於改造成功,而效果也不負衆望,項目啓動速度由原來的 25 s 如坐 🚀一般躍升爲 2 s,簡直誇張。雖然也出現了一些諸如首屏、懶加載性能下降等負面效果,但整體來說依然利大於弊,開發幸福感提升非常明顯。
接下來通過本文給大家分析一下,具體聊一聊 Vite
的快和慢。
Vite 的快
Vite
的快,主要體現在兩個方面: 快速的冷啓動和快速的熱更新。而 Vite
之所以能有如此優秀的表現,完全歸功於 Vite
藉助了瀏覽器對 ESM
規範的支持,採取了與 Webpack
完全不同的 unbundle
機制。
在本章節,我們將通過一個實際的項目,分別使用 Webpack
和 Vite
啓動 dev server
, 給大家展示一下 Vite
的威力。
快速的冷啓動
由於是公司的內部項目,不方便將源代碼上傳到 github
,所以我們只能通過 gif
動圖的方式給大家展示 Webpack
和 Vite
啓動 dev server
的過程。
- Webpack
首先是通過 Webpack
啓動 dev server
,過程如下:
一個規模不是很大的項目,dev server
啓動完成,居然花了 25
s 左右時間。如果項目持續迭代變得再大一點,那每次啓動 dev server
就是一種折磨了。
這個問題,主要是由 Webpack
內部的核心機制 - bundle
模式引發的。
Webpack
能大行其道,歸功於它劃時代的採用了 bundle
機制。通過這種 bundle
機制,Webpack
可以將項目中各種類型的源文件轉化供瀏覽器識別的 js
、css
、img
等文件,建立源文件之間的依賴關係,並將數量龐大的源文件合併爲少量的幾個輸出文件。
bundle
工作機制的核心部分分爲兩塊:構建模塊依賴圖 - module graph
和將 module graph
分解爲最終供瀏覽器使用的幾個輸出文件。
構建 module graph
的過程可以簡單歸納爲:
-
獲取配置文件中
entry
對應的url
(這個url
一般爲相對路徑); -
resolve
- 將url
解析爲絕對路徑,找到源文件在本地磁盤的位置,並構建一個module
對象; -
load
- 讀取源文件的內容; -
transform
- 使用對應的loader
將源文件內容轉化爲瀏覽器可識別的類型; -
parse
- 將轉化後的源文件內容解析爲AST
對象,分析AST
對象,找到源文件中的靜態依賴 (import xxx from 'xxx'
) 和動態依賴 (import('xx')
) 對應的url
, 並收集到module
對象中; -
遍歷第
5
步收集到的靜態依賴、動態依賴對應的url
,重複2
-6
步驟,直到項目中所有的源文件都遍歷完成。
分解 module graph
的過程也可以簡單歸納爲:
-
預處理
module graph
,對module graph
做tree shaking
; -
遍歷
module graph
,根據靜態、動態依賴關係,將module graph
分解爲initial chunk
、async chunks
; -
優化
initial chunk
、async chunks
中重複的module
; -
根據
optimization.splitChunks
進行優化,分離第三方依賴、被多個chunk
共享的module
到common chunks
中; -
根據
chunk
類型,獲取對應的template
; -
遍歷每個
chunk
中收集的module
,結合template
,爲每個chunk
構建最後的輸出內容; -
將最後的構建內容輸出到
output
指定位置;
Webpack
的這種 bundle
機制,奠定了現代靜態打包器 (如 Rollup
、Parcel
、Esbuild
) 的標準工作模式。
然而成也蕭何敗蕭何,強大的 bundle
機制,也引發了構建速度緩慢的問題,而且項目規模越大,構建速度越是緩慢。
其主要原因是構建 module graph
的過程中,涉及到大量的文件 IO
、文件 transfrom
、文件 parse
操作;以及分解 module graph
的過程中,需要遍歷 module graph
、文件 transform
、文件 IO
等。這些操作,往往需要消耗大量的時間,導致構建速度變得緩慢。
開發模式下,dev server
需要 Webpack
完成整個工作鏈路纔可以啓動成功,這就導致構建過程耗時越久,dev server
啓動越久。
爲了加快構建速度,Webpack
也做了大量的優化,如 loader
的緩存功能、webpack5
的持久化緩存等,但這些都治標不治本,只要 Webpack
的核心工作機制不變,那 dev server
啓動優化,依舊是一個任重道遠的過程 (基本上永遠都達不到 Vite
那樣的效果)。
- Vite
同樣的項目,這次換 Vite
啓動。
通過 gif
動圖,我們可以看到 dev server
的啓動速度僅僅需要 2s
左右,相比 Webpack
如 🐢 爬行一樣的速度,就如同坐 🚀一般,開發幸福感頓時拉滿。
Vite
之所以在 dev server
啓動方面,如此給力,是因爲它採取了與 Webpack
截然不同的 unbundle
機制。
unbundle
機制,顧名思義,不需要做 bundle
操作,即不需要構建、分解 module graph
,源文件之間的依賴關係完全通過瀏覽器對 ESM
規範的支持來解析。這就使得 dev server
在啓動過程中只需做一些初始化的工作,剩下的完全由瀏覽器支持。這和 Webpack
的 bundle
機制一比,簡直就是降維打擊,都有點欺負人了 😂。
那有的同學就會問,源文件的 resolve
、load
、transform
、parse
什麼時候做呢 ?
答案是瀏覽器發起請求以後,dev server
端會通過 middlewares
對請求做攔截,然後對源文件做 resolve
、load
、transform
、parse
操作,然後再將轉換以後的內容發送給瀏覽器。
這樣,通過 unbundle
機制, Vite
便可以在 dev server
啓動方面獲取遠超於 Webpack
的優秀體驗。
最後再總結一下, unbundle
機制的核心:
-
模塊之間的依賴關係的解析由瀏覽器實現;
-
文件的轉換由
dev server
的middlewares
實現並做緩存; -
不對源文件做合併捆綁操作;
快速的熱更新
除了 dev server
啓動外, Vite
在熱更新方面也有非常優秀的表現。
我們還是通過同一個項目,對 Webpack
和 Vite
的熱更新做一下比較。
- Webpack
首先是 Webpack
在熱更新方面的表現。
觀察 gif
動圖,修改源文件以後,Webpack
發生耗時大概 5
s 的重新編譯打包過程。
dev server
啓動以後,會 watch
源文件的變化。當源文件發生變化後,Webpack
會重新編譯打包。這個時候,由於我們只修改了一個文件,因此只需要對這個源文件做 resolve
、 load
、 transfrom
、parse
操作,依賴的文件直接使用緩存,因此 dev server
的響應速度比冷啓動要好很多。
dev server
重新編譯打包以後,會通過 ws
連接通知瀏覽器去獲取新的打包文件,然後對頁面做局部更新。
- Vite
再來看看 Vite
在熱更新方面的表現。
觀察 gif
動圖,可以發現 Vite
在熱更新方面也是碾壓 Webpack
。
由於 Vite
採用 unbundle
機制,所以 dev server
在監聽到文件發生變化以後,只需要通過 ws
連接通知瀏覽器去重新加載變化的文件,剩下的工作就交給瀏覽器去做了。(忍不住要給 Vite
點個 👍🏻 了。)
綜上, Vite
在 dev server
冷啓動和熱更新方面,對 Webpack
的優勢實在是太明顯了,難怪會受到大家的青睞。
Vite 的慢
和 bundle
機制有利有弊一樣,unbundle
機制給 Vite
在 dev server
方面獲得巨大性能提升的同時,也帶來一些負面影響,那就是首屏
和懶加載
性能的下降。
在本章節,我們還是通過相同的項目爲大家一一展示。
首屏性能
我們先來對比一下 Webpack
和 Vite
在首屏方面的表現。
- Webpack
Webpack
的首屏 gif
動圖如下:
瀏覽器向 dev server
發起請求, dev server
接受到請求,然後將已經打包構建好的首屏內容發送給瀏覽器。整個過程非常普遍,沒有什麼可說的,不存在什麼性能問題。
- Vite
相比 Webpack
, Vite
在首屏方面的表現就有些差了。
通過 gif
動圖,我們可以很明顯的看到首屏需要較長的時間才能完全顯示。
由於 unbundle
機制,首屏期間需要額外做以下工作:
-
不對源文件做合併捆綁操作,導致大量的
http
請求; -
dev server
運行期間對源文件做resolve
、load
、transform
、parse
操作; -
預構建、二次預構建操作也會阻塞首屏請求,直到預構建完成爲止;
和 Webpack
對比,Vite
把需要在 dev server
啓動過程中完成的工作,轉移到了 dev server
響應瀏覽器請求的過程中,不可避免的導致首屏性能下降。
不過首屏性能差只發生在 dev server
啓動以後第一次加載頁面時發生。之後再 reload
頁面時,首屏性能會好很多。原因是 dev server
會將之前已經完成轉換的內容緩存起來。
懶加載性能
- Webpack
在懶加載方面, Webpack
的表現也正常,沒什麼好說的。
- Vite
同樣的, Vite
在懶加載方面的性能也比 Webpack
差。
和首屏一樣,由於 unbundle
機制,動態加載的文件,需要做 resolve
、load
、transform
、parse
操作,並且還有大量的 http
請求,導致懶加載性能也受到影響。
此外,如果懶加載過程中,發生了二次預構建,頁面會 reload
,對開發體驗也有一定程度的影響。
結束語
儘管在首屏、懶加載性能方面存在一些不足,但瑕不掩瑜,作爲目前最 🔥 的構建工具,Vite
可以說是實至名歸。而且這些問題並非不可解決,比如我們可以通過 prefetch
、持久化緩存
等手段做優化,相信 Vite
未來也會做出對應的改進。
總的來說, Vite
還是未來可期的,還沒有開始使用的小夥伴,可以去嘗試一下噢,😄。
作者:0o 華仔 o0
https://juejin.cn/post/7129041114174062628
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Whoqr_DneYXhpY7dJeFXkA