Webpack 的第一次發(fā)布是在 2013 年發(fā)布,長(zhǎng)久以來(lái)是主流的前端打包工具。Vite 的第一次發(fā)布是在 2021 年,是近兩年來(lái)前端打包工具中的后起之秀,重點(diǎn)解決 Webpack 在開(kāi)發(fā)階段的開(kāi)發(fā)痛點(diǎn)。截止 2022.6,Webpack 的 Github Star 數(shù) 61.2k,Vite 的 Github Star 數(shù)是 42.7k。雖然 Vite 剛剛發(fā)布 2 年,但是熱度可見(jiàn)一斑。
下面我們來(lái)對(duì) Webpack 和 Vite 的不同點(diǎn)進(jìn)行比較,并且解釋 Vite 之于 Webpack 性能優(yōu)勢(shì)來(lái)源于哪里?
一、Webpack
Webpack 是一個(gè)基于打包器的構(gòu)建工具,同一個(gè)入口文件的代碼會(huì)打包成一個(gè) Bundle 文件。Webpack 長(zhǎng)期來(lái)的一個(gè)痛點(diǎn)是對(duì)于大規(guī)模應(yīng)用的應(yīng)用啟動(dòng)和熱更新速度很慢。

當(dāng)文件發(fā)生變動(dòng)時(shí),整個(gè) JAVAScript Bundle 文件會(huì)被 Webpack 重新構(gòu)建,這也是為什么使用 Webpack 的大規(guī)模應(yīng)用在應(yīng)用啟動(dòng)和熱更新時(shí)速度很慢的原因。這給進(jìn)行大規(guī)模 JavaScript 應(yīng)用的開(kāi)發(fā)者造成了很差的開(kāi)發(fā)體驗(yàn)。
Webpack 如何工作?
Webpack 打包過(guò)程:
- 從一個(gè)入口文件開(kāi)始,基于代碼文件中的所有 import,export,require 構(gòu)建依賴(lài)樹(shù);
- 編譯 JS/css 等模塊;
- 使用算法排序、重寫(xiě)、連接代碼;
- 優(yōu)化。
開(kāi)發(fā)環(huán)境的 Webpack:
- 打包所有代碼;
- 啟動(dòng) webpack-dev-server 托管打包好的代碼;
- 啟動(dòng) websocket 處理熱更新 HMR。
應(yīng)用規(guī)模越大,啟動(dòng)和熱更新代碼越慢。及時(shí)啟動(dòng)了熱更新,每次代碼變更也需要重新生產(chǎn) Bundle 文件。
二、Vite
Vite 是旨在提升開(kāi)發(fā)者體驗(yàn)的下一代 JavaScript 構(gòu)建工具,核心借助了瀏覽器的原生 ES Modules 和像 esbuild 這樣的將代碼編譯成 native code 的打包工具。
Vite 主要有兩方面組成:
- 一個(gè)開(kāi)發(fā)服務(wù)器,基于 ESM 提供豐富的內(nèi)建能力,比如速度快到驚人的模塊熱更新(HMR);
- 一套構(gòu)建指令,使用 rollup 進(jìn)行代碼打包,且零配置即可輸出用于生產(chǎn)環(huán)境的高度優(yōu)化的靜態(tài)代碼。
Vite 的核心能力和 webpack + webpack-dev-server 相似,但是在開(kāi)發(fā)者體驗(yàn)上有一些提升:
- 無(wú)論項(xiàng)目大小有多大,啟動(dòng)應(yīng)用都只需更少的時(shí)間;
- 無(wú)論項(xiàng)目大小有多大,HMR(Hot Module Replacing)熱更新都可以做到及時(shí)響應(yīng);
- 按需編譯;
- 零配置,開(kāi)箱即用;
- Esbuild 能力帶來(lái)的 Typescript/jsx 的原生支持。
大型的 JavaScript 項(xiàng)目在開(kāi)發(fā)和生產(chǎn)環(huán)境有比較差的性能表現(xiàn),往往是因?yàn)槲覀兪褂玫臉?gòu)建工具沒(méi)有充分做到并行處理、內(nèi)存優(yōu)化和緩存。
2.1 核心理念:Bundless 開(kāi)發(fā)環(huán)境構(gòu)建
瀏覽器的原生 ES Modules 能力允許在不將代碼打包到一起的情況下運(yùn)行 JavaScript 應(yīng)用。Vite 的核心理念很簡(jiǎn)單,就是借助瀏覽器原生 ES Modules 能力,當(dāng)瀏覽器發(fā)出請(qǐng)求時(shí),為瀏覽器按需提供 ES Module 文件,瀏覽器獲取 ES Module 文件會(huì)直接執(zhí)行。
2.2 應(yīng)用啟動(dòng)
Vite 將應(yīng)用中的模塊分為依賴(lài)和源碼兩類(lèi),分別進(jìn)行服務(wù)器啟動(dòng)時(shí)間的優(yōu)化。
- 依賴(lài)模塊,開(kāi)發(fā)過(guò)程中基本不會(huì)變化。Vite 對(duì)依賴(lài)采用了 esbuild 預(yù)構(gòu)建的方式,esbuild 使用 Go 編寫(xiě),并且比以 JavaScript 編寫(xiě)的打包器預(yù)構(gòu)建依賴(lài)快 10-100 倍;
- 源碼模塊,是用戶(hù)自己開(kāi)發(fā)的代碼,會(huì)經(jīng)常變動(dòng)。
Vite 在瀏覽器請(qǐng)求時(shí)按需轉(zhuǎn)換并以原生 ESM 方式提供源碼,讓瀏覽器接管了打包程序的部分工作。
2.3 Vite 如何工作?
Vite 通過(guò)原生 ES Modules 托管源代碼,本質(zhì)上是讓瀏覽器來(lái)接管部分打包器的工作。Vite 只會(huì)在瀏覽器請(qǐng)求發(fā)生時(shí),按需將源碼轉(zhuǎn)成 ES Modules 格式返回給瀏覽器,由瀏覽器加載并執(zhí)行 ES Modules 文件。

2.4 熱更新
在基于 Bundle 構(gòu)建的構(gòu)建器中,當(dāng)一個(gè)文件變動(dòng)時(shí),重新構(gòu)建整個(gè) Bundle 文件是非常低效的,且隨著應(yīng)用規(guī)模的上升,構(gòu)建速度會(huì)直線下降。
傳統(tǒng)的構(gòu)建器雖然提供了熱更新的能力,但是也會(huì)存在隨著應(yīng)用規(guī)模上升,熱更新速度顯著下降的問(wèn)題。
Vite 基于 ESM 按需提供源碼文件,當(dāng)一個(gè)文件被編輯后,Vite 只會(huì)重新編譯并提供該文件。因此,無(wú)論項(xiàng)目規(guī)模多大,Vite 的熱更新都可以保持快速更新。
此外,Vite 合理利用瀏覽器緩存來(lái)加速頁(yè)面加載,源碼模塊請(qǐng)求根據(jù) 304 Not Modified 進(jìn)行協(xié)商緩存;依賴(lài)模塊請(qǐng)求通過(guò) Cache-Control: max-age=31536000,immutable 進(jìn)行強(qiáng)緩存,因此一旦緩存,不會(huì)再次請(qǐng)求。
2.5 生產(chǎn)環(huán)境仍需打包
在生產(chǎn)環(huán)境使用 ESM 會(huì)存在大量額外網(wǎng)絡(luò)請(qǐng)求問(wèn)題,因此生產(chǎn)環(huán)境不太試用 ESM,最好的方式還是代碼進(jìn)行 tree-shaking、懶加載、和 chunk 分隔等。
那么生產(chǎn)環(huán)境的構(gòu)建為什么不直接使用 esbuild,而是使用 rollup 呢?這是因?yàn)?esbuild 在代碼分隔、css 處理等方面的功能仍在開(kāi)發(fā)中,rollup 在應(yīng)用打包方面更加的成熟且靈活。
2.6 性能提升
Vite 依托支持原生 ESM 模塊的現(xiàn)代瀏覽器,極大的降低了應(yīng)用的啟動(dòng)和重新構(gòu)建時(shí)間。Vite 本質(zhì)上是一個(gè)在開(kāi)發(fā)環(huán)境為瀏覽器按需提供文件的 Web Server,這些文件包含源碼模塊和在第一次運(yùn)行時(shí)使用 esbuild 預(yù)構(gòu)建的依賴(lài)模塊。
Vite 和 Webpack 的主要不同在于開(kāi)發(fā)環(huán)境下對(duì)于源碼如何被托管以及支持哪種模塊規(guī)范。
2.7 依賴(lài)預(yù)構(gòu)建
Vite 在首次啟動(dòng)時(shí),會(huì)進(jìn)行依賴(lài)預(yù)構(gòu)建。依賴(lài)預(yù)構(gòu)建有兩個(gè)目的:
- CommonJs 和 UMD 的兼容性:開(kāi)發(fā)階段,Vite 的 Dev Server 將所有代碼視為原生 ES 模塊。因此,Vite 必須將 CommonJS 或 UMD 發(fā)布的依賴(lài)項(xiàng)轉(zhuǎn)為 ESM。
- 性能:Vite 將有很多內(nèi)部模塊的依賴(lài)視為單個(gè)模塊,以提升頁(yè)面加載性能。比如,lodash-es 擁有超過(guò) 600 個(gè)內(nèi)部模塊,當(dāng) import {debounce} from 'lodash-es'; 時(shí),瀏覽器會(huì)同時(shí)發(fā)起超過(guò) 600 個(gè)請(qǐng)求,并行請(qǐng)求過(guò)多將會(huì)顯著影響頁(yè)面加載性能。因此預(yù)構(gòu)建將 lodash-es 視為一個(gè)模塊,瀏覽器只需要發(fā)起一個(gè)請(qǐng)求。
2.8 緩存
2.8.1 文件系統(tǒng)緩存
Vite 會(huì)將預(yù)構(gòu)建的依賴(lài)緩存到 node_modules/.vite ,它根據(jù)幾個(gè)源決定是否需要重新運(yùn)行預(yù)構(gòu)建步驟:
- package.json 中的 dependencies 列表;
- 包管理的 lockfile,例如 package-lock.json,yarn.lock 或者 pnpm-lock.yaml
- 可能在 vite.config.js 相關(guān)字段中配置過(guò)的。
只有在上述其中一項(xiàng)發(fā)生更改時(shí),才需要重新運(yùn)行預(yù)構(gòu)建。
如果處于某些原因,你想要強(qiáng)制 Vite 重新構(gòu)建依賴(lài),你可以用 --force 命令選項(xiàng)啟動(dòng)開(kāi)發(fā)服務(wù)器,或者手動(dòng)刪除 node_modules/.vite 目錄。
2.8.2 瀏覽器緩存
解析后的依賴(lài)請(qǐng)求會(huì)以 HTTP 頭 max-age=31536000,immutable 強(qiáng)緩存,以提高開(kāi)發(fā)時(shí)的頁(yè)面重載性能。如果你想通過(guò)本地編輯來(lái)調(diào)試依賴(lài)項(xiàng),可以:
- 通過(guò)瀏覽器調(diào)試工具的.NETwork 選項(xiàng)卡暫時(shí)禁用緩存;
- 重啟 Vite Dev Server,并添加 --force 命令以重新構(gòu)建依賴(lài);
- 重新載入頁(yè)面。
2.9 Typescript 原生支持
Vite 天然支持引入 .ts 文件,單僅支持 .ts 文件的轉(zhuǎn)譯工作,并不執(zhí)行任何類(lèi)型檢查。
Vite 使用 esbuild 將 TypeScript 轉(zhuǎn)譯到 JavaScript,約是 tsc 速度的 20-30 倍,同時(shí) HMR 更新到瀏覽器的時(shí)間小于 50 ms。
三、對(duì)比
簡(jiǎn)單對(duì) Webpack 和 Vite 進(jìn)行一個(gè)對(duì)比:
3.1 Webpack
- 支持的模塊規(guī)范:ES Modules,CommonJS 和 AMD Modules;
- Dev Server:通過(guò) webpack-dev-server 托管打包好的模塊;
- 生產(chǎn)環(huán)境構(gòu)建:webpack
3.2 Vite
- 支持的模塊規(guī)范:ES Modules;
- Dev Server:原生 ES Modules;
- 生產(chǎn)環(huán)境構(gòu)建:Rollup
四、總結(jié)
由于瀏覽器原生 ES Modules 的支持,當(dāng)瀏覽器發(fā)出請(qǐng)求時(shí),Vite 可以在不將源碼打包為一個(gè) Bundle 文件的情況下,將源碼文件轉(zhuǎn)化為 ES Modules 文件之后返回給瀏覽器。這樣 Vite 的應(yīng)用啟動(dòng)和熱更新 HMR 時(shí)的速度都不會(huì)隨著應(yīng)用規(guī)模的增加而變慢。
文章來(lái)源:
https://juejin.cn/post/7106136866381889573