Webpack 的第一次發布是在 2013 年發布,長久以來是主流的前端打包工具。Vite 的第一次發布是在 2021 年,是近兩年來前端打包工具中的后起之秀,重點解決 Webpack 在開發階段的開發痛點。截止 2022.6,Webpack 的 Github Star 數 61.2k,Vite 的 Github Star 數是 42.7k。雖然 Vite 剛剛發布 2 年,但是熱度可見一斑。
下面我們來對 Webpack 和 Vite 的不同點進行比較,并且解釋 Vite 之于 Webpack 性能優勢來源于哪里?
一、Webpack
Webpack 是一個基于打包器的構建工具,同一個入口文件的代碼會打包成一個 Bundle 文件。Webpack 長期來的一個痛點是對于大規模應用的應用啟動和熱更新速度很慢。
當文件發生變動時,整個 JAVAScript Bundle 文件會被 Webpack 重新構建,這也是為什么使用 Webpack 的大規模應用在應用啟動和熱更新時速度很慢的原因。這給進行大規模 JavaScript 應用的開發者造成了很差的開發體驗。
Webpack 如何工作?
Webpack 打包過程:
- 從一個入口文件開始,基于代碼文件中的所有 import,export,require 構建依賴樹;
- 編譯 JS/css 等模塊;
- 使用算法排序、重寫、連接代碼;
- 優化。
開發環境的 Webpack:
- 打包所有代碼;
- 啟動 webpack-dev-server 托管打包好的代碼;
- 啟動 websocket 處理熱更新 HMR。
應用規模越大,啟動和熱更新代碼越慢。及時啟動了熱更新,每次代碼變更也需要重新生產 Bundle 文件。
二、Vite
Vite 是旨在提升開發者體驗的下一代 JavaScript 構建工具,核心借助了瀏覽器的原生 ES Modules 和像 esbuild 這樣的將代碼編譯成 native code 的打包工具。
Vite 主要有兩方面組成:
- 一個開發服務器,基于 ESM 提供豐富的內建能力,比如速度快到驚人的模塊熱更新(HMR);
- 一套構建指令,使用 rollup 進行代碼打包,且零配置即可輸出用于生產環境的高度優化的靜態代碼。
Vite 的核心能力和 webpack + webpack-dev-server 相似,但是在開發者體驗上有一些提升:
- 無論項目大小有多大,啟動應用都只需更少的時間;
- 無論項目大小有多大,HMR(Hot Module Replacing)熱更新都可以做到及時響應;
- 按需編譯;
- 零配置,開箱即用;
- Esbuild 能力帶來的 Typescript/jsx 的原生支持。
大型的 JavaScript 項目在開發和生產環境有比較差的性能表現,往往是因為我們使用的構建工具沒有充分做到并行處理、內存優化和緩存。
2.1 核心理念:Bundless 開發環境構建
瀏覽器的原生 ES Modules 能力允許在不將代碼打包到一起的情況下運行 JavaScript 應用。Vite 的核心理念很簡單,就是借助瀏覽器原生 ES Modules 能力,當瀏覽器發出請求時,為瀏覽器按需提供 ES Module 文件,瀏覽器獲取 ES Module 文件會直接執行。
2.2 應用啟動
Vite 將應用中的模塊分為依賴和源碼兩類,分別進行服務器啟動時間的優化。
- 依賴模塊,開發過程中基本不會變化。Vite 對依賴采用了 esbuild 預構建的方式,esbuild 使用 Go 編寫,并且比以 JavaScript 編寫的打包器預構建依賴快 10-100 倍;
- 源碼模塊,是用戶自己開發的代碼,會經常變動。
Vite 在瀏覽器請求時按需轉換并以原生 ESM 方式提供源碼,讓瀏覽器接管了打包程序的部分工作。
2.3 Vite 如何工作?
Vite 通過原生 ES Modules 托管源代碼,本質上是讓瀏覽器來接管部分打包器的工作。Vite 只會在瀏覽器請求發生時,按需將源碼轉成 ES Modules 格式返回給瀏覽器,由瀏覽器加載并執行 ES Modules 文件。
2.4 熱更新
在基于 Bundle 構建的構建器中,當一個文件變動時,重新構建整個 Bundle 文件是非常低效的,且隨著應用規模的上升,構建速度會直線下降。
傳統的構建器雖然提供了熱更新的能力,但是也會存在隨著應用規模上升,熱更新速度顯著下降的問題。
Vite 基于 ESM 按需提供源碼文件,當一個文件被編輯后,Vite 只會重新編譯并提供該文件。因此,無論項目規模多大,Vite 的熱更新都可以保持快速更新。
此外,Vite 合理利用瀏覽器緩存來加速頁面加載,源碼模塊請求根據 304 Not Modified 進行協商緩存;依賴模塊請求通過 Cache-Control: max-age=31536000,immutable 進行強緩存,因此一旦緩存,不會再次請求。
2.5 生產環境仍需打包
在生產環境使用 ESM 會存在大量額外網絡請求問題,因此生產環境不太試用 ESM,最好的方式還是代碼進行 tree-shaking、懶加載、和 chunk 分隔等。
那么生產環境的構建為什么不直接使用 esbuild,而是使用 rollup 呢?這是因為 esbuild 在代碼分隔、css 處理等方面的功能仍在開發中,rollup 在應用打包方面更加的成熟且靈活。
2.6 性能提升
Vite 依托支持原生 ESM 模塊的現代瀏覽器,極大的降低了應用的啟動和重新構建時間。Vite 本質上是一個在開發環境為瀏覽器按需提供文件的 Web Server,這些文件包含源碼模塊和在第一次運行時使用 esbuild 預構建的依賴模塊。
Vite 和 Webpack 的主要不同在于開發環境下對于源碼如何被托管以及支持哪種模塊規范。
2.7 依賴預構建
Vite 在首次啟動時,會進行依賴預構建。依賴預構建有兩個目的:
- CommonJs 和 UMD 的兼容性:開發階段,Vite 的 Dev Server 將所有代碼視為原生 ES 模塊。因此,Vite 必須將 CommonJS 或 UMD 發布的依賴項轉為 ESM。
- 性能:Vite 將有很多內部模塊的依賴視為單個模塊,以提升頁面加載性能。比如,lodash-es 擁有超過 600 個內部模塊,當 import {debounce} from 'lodash-es'; 時,瀏覽器會同時發起超過 600 個請求,并行請求過多將會顯著影響頁面加載性能。因此預構建將 lodash-es 視為一個模塊,瀏覽器只需要發起一個請求。
2.8 緩存
2.8.1 文件系統緩存
Vite 會將預構建的依賴緩存到 node_modules/.vite ,它根據幾個源決定是否需要重新運行預構建步驟:
- package.json 中的 dependencies 列表;
- 包管理的 lockfile,例如 package-lock.json,yarn.lock 或者 pnpm-lock.yaml
- 可能在 vite.config.js 相關字段中配置過的。
只有在上述其中一項發生更改時,才需要重新運行預構建。
如果處于某些原因,你想要強制 Vite 重新構建依賴,你可以用 --force 命令選項啟動開發服務器,或者手動刪除 node_modules/.vite 目錄。
2.8.2 瀏覽器緩存
解析后的依賴請求會以 HTTP 頭 max-age=31536000,immutable 強緩存,以提高開發時的頁面重載性能。如果你想通過本地編輯來調試依賴項,可以:
- 通過瀏覽器調試工具的.NETwork 選項卡暫時禁用緩存;
- 重啟 Vite Dev Server,并添加 --force 命令以重新構建依賴;
- 重新載入頁面。
2.9 Typescript 原生支持
Vite 天然支持引入 .ts 文件,單僅支持 .ts 文件的轉譯工作,并不執行任何類型檢查。
Vite 使用 esbuild 將 TypeScript 轉譯到 JavaScript,約是 tsc 速度的 20-30 倍,同時 HMR 更新到瀏覽器的時間小于 50 ms。
三、對比
簡單對 Webpack 和 Vite 進行一個對比:
3.1 Webpack
- 支持的模塊規范:ES Modules,CommonJS 和 AMD Modules;
- Dev Server:通過 webpack-dev-server 托管打包好的模塊;
- 生產環境構建:webpack
3.2 Vite
- 支持的模塊規范:ES Modules;
- Dev Server:原生 ES Modules;
- 生產環境構建:Rollup
四、總結
由于瀏覽器原生 ES Modules 的支持,當瀏覽器發出請求時,Vite 可以在不將源碼打包為一個 Bundle 文件的情況下,將源碼文件轉化為 ES Modules 文件之后返回給瀏覽器。這樣 Vite 的應用啟動和熱更新 HMR 時的速度都不會隨著應用規模的增加而變慢。
文章來源:
https://juejin.cn/post/7106136866381889573