Vite 3.0 更新了什么?
以下文章來源于三元同學(xué) ,作者三元同學(xué)
在 2021 年 2 月,尤大正式推出了 Vite 2.0 版本,可以說是 Vite 的一個重要轉(zhuǎn)折點,自此之后 Vite 的用戶量發(fā)生了非常迅速的增長,很快達到了每周 100 萬的 npm 下載量。同時,Vite 的社區(qū)也越來越活躍,目前已經(jīng)形成非常龐大的社區(qū)生態(tài)(詳情可見Github 地址[1]),給整個前端領(lǐng)域帶來了諸多的改變,如:
Nuxt 3、SvelteKit、Astro、StoryBook 等在內(nèi)的各大前端框架已經(jīng)將 Vite 作為內(nèi)置的構(gòu)建方案。
基于 Vite 的測試工具 Vitest 誕生,成為替代 Jest 的新一代測試方案。
如今已經(jīng) 2022 年 7 月,距離 v2 版本已經(jīng)發(fā)布了 16 個月的時間,Vite 正式推出 3.0 版本,接下來就給大家介紹一下 Vite 3.0 帶來的一些改變以及未來的規(guī)劃。
一、全新的 VitePress 文檔
對于用戶側(cè)來說,談到框架的更新,文檔自然是最重要的部分?,F(xiàn)在你可以直接去 vitejs.dev[2] 站點體驗到 v3 版本的文檔,目前文檔同樣是使用 VitePress[3] 進行搭建。下面是暗黑模式下的一張截圖:
怎么樣,是不是比以前更加好看了呢?
不光是 Vite,也有 Vite 生態(tài)中其它的一些項目使用 VitePress 進行文檔站點的搭建,比如 Vitest[4], vite-plugin-pwa[5] 以及 VitePress[6] 自身的文檔,我也十分推薦大家使用 VitePress 作為自己的文檔建站方案之一。
如果你需要查看 Vite 2.0 的文章,也可以訪問 v2.vitejs.dev[7]。
二、開發(fā)階段的更新
1. CLI 的更新
在執(zhí)行 vite 命令啟動項目時,終端的界面和之前會有所不同,而更重要的是,為了避免 Vite 開發(fā)服務(wù)的端口和別的應(yīng)用沖突,默認(rèn)的端口號從之前的 3000 變成了 5173。
2. 開箱即用的 WebSocket 連接策略
Vite 2 中有存在一個痛點,即在存在代理的情況下(比如 Web IDE)需要我們手動配置 WebSocket 使 HMR 生效。目前 Vite 內(nèi)置了一套更加完善的 WebSocket 連接策略,自動滿足更多場景的 HMR 需求。
3. 服務(wù)冷啟動性能提升
Vite 3.0 在服務(wù)冷啟動方面做了非常多的工作,來最大程度提升項目啟動的速度。
首先我們來盤點一下 Vite 2.x 階段服務(wù)冷啟動的一些問題。
從 Vite 2.0 到 2.9 版本之前,Vite 會在服務(wù)啟動之前進行依賴預(yù)構(gòu)建,也就是使用 Esbuild 將項目中使用到的依賴掃描出來(Scan),然后分別進行一次打包(Optimize)。
這樣會造成兩個問題:
依賴預(yù)構(gòu)建會阻塞 Dev Server 啟動,但其實不阻塞的情況下,Dev Server 也可以正常啟動。
當(dāng)某些 Vite 插件手動注入了 import 語句,比如調(diào)用 babel-plugin-import 添加import Button from 'antd/lib/button',就會導(dǎo)致 Vite 的二次預(yù)構(gòu)建,因為 antd/lib/button 的引入代碼由 Vite 插件注入,屬于 Dev Server 運行時發(fā)現(xiàn)的依賴,冷啟動階段無法掃描到。
所謂的二次預(yù)構(gòu)建包含兩個步驟,一是需要將所有的依賴全量預(yù)構(gòu)建,二是由于依賴更新,頁面需要進行 reload,加載最新的依賴代碼。這樣會導(dǎo)致 Dev Server 性能明顯下降,尤其是在新增依賴較多的場景下,很容易出現(xiàn)瀏覽器卡住的情況。因此二次預(yù)構(gòu)建也是需要極力避免的。當(dāng)時 vite-plugin-optimize-persist[8] 就是為了解決二次預(yù)構(gòu)建帶來的問題,通過持久化的方式記錄 Dev Server 運行時掃描到的依賴,從而讓首次預(yù)構(gòu)建便可以感知到,避免二次預(yù)構(gòu)建的發(fā)生。
到了 2.9 版本,Vite 將預(yù)構(gòu)建的邏輯做了一次整體的重構(gòu),最后的效果是下面這樣的:
Dev Server 啟動后預(yù)構(gòu)建(Optimize 階段)在后臺執(zhí)行,也就是預(yù)構(gòu)建不再阻塞 Dev Server 的啟動,只需要等待 Scan 階段完成,不過通常這個階段的開銷非常小。
如果某些依賴是 Dev Server 運行時才發(fā)現(xiàn)的,那么 Vite 會盡可能地復(fù)用已有預(yù)構(gòu)建產(chǎn)物,盡量不進行 page reload。
具體實現(xiàn)大家可以去查看這個 PR[9]
那問題就完全解決了嗎?其實并不是,在某些場景下,Vite 仍然不可避免地需要二次預(yù)構(gòu)建。如下面的這個例子:
A 和 B 都是項目的第三方依賴,它們也同時依賴 C。那么當(dāng) Vite 預(yù)構(gòu)建 A 的時候,將會 A 和 C 一起進行打包。但 Vite 在運行時發(fā)現(xiàn)了依賴 B,而 A 和 B 需要共享 C 的代碼,這樣 C 的代碼可能就會被抽離成一個公共的 chunk,因此之前 A 的預(yù)構(gòu)建產(chǎn)物可能就發(fā)生變化了,那么此時 Vite 必須要強制刷新頁面,讓瀏覽器使用最新的預(yù)構(gòu)建產(chǎn)物。這仍然是一個二次預(yù)構(gòu)建(所有依賴再次打包 + page reload)的過程。
總體而言,2.9 版本解決了預(yù)構(gòu)建阻塞服務(wù)啟動的問題,但并沒有完全解決二次預(yù)構(gòu)建的問題。
但在 Vite 3.0,二次預(yù)構(gòu)建的問題也得到了根本的解決。那 Vite 3.0 是如何做到的呢?
核心的解決思路在于延遲處理,即把預(yù)構(gòu)建的行為延遲到頁面加載的最后階段進行,此時 Vite 已經(jīng)編譯完了所有的源文件,可以準(zhǔn)確地記錄下所有需要預(yù)構(gòu)建的依賴(包括 Vite 插件添加的一些依賴),然后統(tǒng)一進行預(yù)構(gòu)建,將預(yù)構(gòu)建的產(chǎn)物響應(yīng)給給瀏覽器即可。
依賴預(yù)構(gòu)建的代碼在 Vite 中先后重構(gòu)了多次,目前的版本實現(xiàn)比較復(fù)雜,后續(xù)會單獨寫一篇文章討論實現(xiàn)細(xì)節(jié)。
因此,與 Vite 2.0 相比,Vite 3.0 在冷啟動階段所做的優(yōu)化主要有兩個方面:
預(yù)構(gòu)建不再阻塞 Dev Server 的啟動,真正做到服務(wù)秒啟動的效果;
從根本上防止二次預(yù)構(gòu)建的發(fā)生。
4. import.meta.glob 語法更新
Vite 3.0 中重寫對 import.meta.glob 的實現(xiàn)進行了重寫,支持了更加靈活的 glob 語法,增加了如下的一些特性:
多種模式匹配:
import.meta.glob(["./dir/*.js", "./another/*.js"]);
否定模式(!):
import.meta.glob(["./dir/*.js", "!**/bar.js"]);
命名導(dǎo)入,可以更好地做到 Tree Shaking:
import.meta.glob("./dir/*.js", { import: "setup" });
自定義 query 參數(shù):
import.meta.glob("./dir/*.js", { query: { custom: "data" } });
指定 eager 模式,替換掉原來import.meta.globEager:
import.meta.glob("./dir/*.js", { eager: true });
三、生產(chǎn)階段的更新
1. SSR 產(chǎn)物默認(rèn)使用 ESM 格式
在當(dāng)下的社區(qū)生態(tài)中,眾多 SSR 框架已經(jīng)在使用 ESM 格式作為默認(rèn)的產(chǎn)物格式。Vite 3.0 也積極擁抱社區(qū),支持 SSR 構(gòu)建默認(rèn)打包出 ESM 格式的產(chǎn)物。
2. Relative Base 支持
Vite 3.0 正式支持 Relative Base(即配置base: ''),主要用于構(gòu)建時無法確定 base 地址的場景。
四、實驗性功能
1. 更細(xì)粒度的 base 配置
在某些場景下,我們需要將不同的資源部署到不同的 CDN 上,比如將圖片部署到單獨的 CDN,和 JS/CSS 的部署服務(wù)區(qū)分開來。但 2.x 的版本僅支持統(tǒng)一的部署域名,即base 配置。在 3.0 中,你可以通過 renderBuiltUrl 進行更細(xì)粒度的配置:
{
experimental: {
renderBuiltUrl: (filename: string, { hostType: 'js' | 'css' | 'html' }) => {
if (hostType === 'js') {
return { runtime: `window.__toCdnUrl(${JSON.stringify(filename)})` }
} else {
return 'https://cdn.domain.com/assets/' + filename
}
}
}
}
目前該配置項還不穩(wěn)定 ,可能會在之后的 minor 版本修改。具體文檔見 https://vitejs.dev/guide/build.html#advanced-base-options
2. Esbuild 預(yù)構(gòu)建用于生產(chǎn)環(huán)境
這應(yīng)該是 Vite 架構(gòu)上非常大的一個改動: 將原來僅僅用于開發(fā)階段的依賴預(yù)構(gòu)建功能應(yīng)用在生產(chǎn)環(huán)境。在 Vite 2.x 中,開發(fā)階段使用 Esbuild 來打包依賴,而在生產(chǎn)環(huán)境使用 Rollup 進行打包,用 @rollupjs/plugin-commonjs 來處理 cjs 的依賴,這樣做會導(dǎo)致依賴處理的不一致問題,造成一些生產(chǎn)構(gòu)建中的 bug。
但 Vite 3.0 中支持通過配置將 Esbulid 預(yù)構(gòu)建同時用于開發(fā)環(huán)境和生產(chǎn)環(huán)境,僅添加optimizeDeps.disabled: false 的配置即可。不過這個改動確實比較大,Vite 團隊不打算將此作為 v3 的正式更新內(nèi)容,而是一個實驗性質(zhì)的功能,不會默認(rèn)開啟。
順便提一句,Rollup 將在接下來的幾個月發(fā)布 v3 的大版本,要知道,Rollup 2.0 發(fā)布至今已經(jīng)過去 2 年多的時間了,無論是 Rollup 還是 Vite 來講,這都是一次非常重大的變更。由于 Vite 的架構(gòu)非常依賴 Rollup,在 Rollup 發(fā)布 v3 之后,Vite 也將跟隨著發(fā)布 Vite 的第 4 個 major 版本。所以,Vite 4.0 的到來也不遠啦:)
五、倉庫內(nèi)部的變化
除了本身功能上的演進,Vite 的倉庫本身也產(chǎn)生了不少的變化,從中我們也能了解到社區(qū)的一些動向:
不再支持 Nodejs 12,需要 Node.js 14.18+ 的版本。
單元測試和 E2E 測試從 Jest 完全遷移到 Vitest,一方面 Vitest 更快、體驗更好,另一方面也能在 Vite 這樣大型的倉庫完善 Vitest 的生態(tài),進一步提升 Vitest 穩(wěn)定性。
VitePress 文檔部分也參與 CI 流程。
包管理器 pnpm 遷移至 v7。
不管是 Vite 本身的包還是 E2E 中測試的項目,都在 package.json 中聲明 type: "module",即 Pure ESM 包,對外提供 ESM 格式的產(chǎn)物,將社區(qū) Pure ESM 的趨勢又推動了一步。
官方所有的 Vite 插件都采用 unbuild(新一代庫構(gòu)建工具) 進行構(gòu)建,pluin-vue-jsx 和 plugin-legacy 均遷移到了 TS 上。
包體積優(yōu)化。3.0 進一步優(yōu)化 Vite 本身的產(chǎn)物和 node_modules 體積,將 terser 和 node-forge 的依賴移除,讓用戶進行按需安裝(node-forge 的功能是實現(xiàn) https 證書生成,可用 @vitejs/plugin-basic-ssl 插件替代),效果如下:
Publish Size Install Size
Vite 2.9.14 4.38MB 19.1MB
Vite 3.0.0 3.05MB 17.8MB
Reduction -30% -7%
不得不說在自身包體積的優(yōu)化方面, Vite 對于還是做的很細(xì)致的,這也是很多庫開發(fā)者忽視的一點,有時候加個插件就得安裝動輒上百 MB 的依賴,導(dǎo)致項目的 node_modules 最后變得非常臃腫,此時不妨學(xué)習(xí)一下 Vite 是怎么優(yōu)化自身體積的。
六、未來規(guī)劃
首先在 Vite 3.0 發(fā)布之后會重點保證 3.0 的穩(wěn)定性,解決目前的一系列 issue。
其次,Rollup 團隊將在接下來的幾個月發(fā)布新的 major 版本,Vite 將持續(xù)跟進,緊接著發(fā)布 v4 版本,并在 v4 版本中將目前的一些實踐性功能穩(wěn)定下來。
小結(jié)
Vite 3.0 帶來了一些比較大的架構(gòu)變動,比如依賴預(yù)構(gòu)建的重構(gòu)、支持生產(chǎn)環(huán)境 Esbuild 預(yù)打包依賴以及全面支持 Pure ESM,當(dāng)然也有一些比較小的 break change 在這個版本集中發(fā)布,比如 import.meta.glob 語法的變更等等。
總之,在這一年多的時間里,Vite 團隊做了非常多的功能改進和架構(gòu)升級,目前的 Github Star 已經(jīng)達到了 44 k+,并且還在持續(xù)維護中。與此同時,Vite 的社區(qū)生態(tài)也逐步完善,比如 Vitest、VitePress、豐富的社區(qū)插件[10]以及眾多內(nèi)置 Vite 的社區(qū)框架等等,可以預(yù)見的是,Vite 將在未來的很長一段時間內(nèi)繼續(xù)發(fā)展,持續(xù)迭代,提供更好的用戶體驗,成為下一代前端工具鏈。
參考資料
[1]
Github 地址: https://github.com/vitejs/awesome-vite
[2]
vitejs.dev: vitejs.dev
[3]
VitePress: https://vitepress.vuejs.org/
[4]
Vitest: https://vitest.dev/
[5]
vite-plugin-pwa: https://vite-plugin-pwa.netlify.app/
[6]
VitePress: https://vitepress.vuejs.org/
[7]
v2.vitejs.dev: https://v2.vitejs.dev/
[8]
vite-plugin-optimize-persist: https://github.com/antfu/vite-plugin-optimize-persist
[9]
PR: https://github.com/vitejs/vite/pull/6758
[10]
社區(qū)插件: https://github.com/vitejs/awesome-vite
作者:三元同學(xué)
歡迎關(guān)注微信公眾號 :前端印象