爲什麼有人說 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