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