Vite 的定義
Vite 是面向現(xiàn)代瀏覽器的一個(gè)更輕、更快的 Web 應(yīng)用開發(fā)工具,核心基于 ECMAScript 標(biāo)準(zhǔn)原生模塊系統(tǒng)(ES Modules)實(shí)現(xiàn)。
從表象功能上看,Vite 可以取代基于 Webpack 的 vue-cli 或者 cra 的集成式開發(fā)工具,提供全新的一種開發(fā)體驗(yàn)。
Vite 的由來(lái)
在此之前,如果我們所開發(fā)的應(yīng)用比較復(fù)雜(代碼量偏大),使用 Webpack 的開發(fā)過(guò)程相對(duì)沒有那么「絲滑」,具體表現(xiàn)為以下兩點(diǎn):
Webpack Dev Server 冷啟動(dòng)時(shí)間會(huì)比較長(zhǎng),稍大一點(diǎn)的項(xiàng)目啟動(dòng)開發(fā)服務(wù)都需要等待 10 - 20 秒;
Webpack HMR 熱更新的反應(yīng)速度比較慢,修改完代碼需要等待編譯器全部編譯完成才能開始同步到瀏覽器;
快速上手
這里我們?cè)挷欢嗾f(shuō),先上手體驗(yàn)一下 Vite,然后再來(lái)分析其內(nèi)部的思路和想法。
Vite 官方目前提供了一個(gè)比較簡(jiǎn)單的腳手架:create-vite-app,可以使用這個(gè)腳手架快速創(chuàng)建一個(gè)使用 Vite 構(gòu)建的 Vue.js 應(yīng)用
$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
如果使用 yarn:
$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev
P.S.
npm init 或者 yarn create 是這兩個(gè)包管理工具提供的新功能,其內(nèi)部就是自動(dòng)去安裝一個(gè) create-<xxx> 的模塊(臨時(shí)),然后自動(dòng)執(zhí)行這個(gè)模塊中的 bin。例如:yarn create react-app my-react-app 就相當(dāng)于先 yarn global add create-react-app,然后自動(dòng)執(zhí)行 create-react-app my-react-app。
對(duì)比差異點(diǎn)
打開生成的項(xiàng)目過(guò)后,你會(huì)發(fā)現(xiàn)就是一個(gè)很普通的 Vue.js 應(yīng)用,沒有太多特殊的地方。
不過(guò)相比于之前 vue-cli 創(chuàng)建的項(xiàng)目或者是基于 Webpack 搭建的 Vue.js 項(xiàng)目,這里的開發(fā)依賴非常簡(jiǎn)單,只有 vite 和 @vue/compiler-sfc。
{
"name": "vite-demo",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"vue": "^3.0.0-rc.1"
},
"devDependencies": {
"vite": "^1.0.0-rc.1",
"@vue/compiler-sfc": "^3.0.0-rc.1"
}
}
Vite 就是我們今天要介紹的主角,而 @vue/compiler-sfc 就是用來(lái)編譯我們項(xiàng)目中 .vue 結(jié)尾的單文件組件(SFC),它取代的就是 Vue.js 2.x 時(shí)使用的 vue-template-compiler。
再者就是 Vue.js 的版本是 3.0。這里尤其需要注意:Vite 目前只支持 Vue.js 3.0 版本。
在vue中,因?yàn)?Webpack Dev Server 在啟動(dòng)時(shí),需要先 build 一遍,而 build 的過(guò)程是需要耗費(fèi)很多時(shí)間的。

而 Vite 則完全不同,當(dāng)我們執(zhí)行 vite serve 時(shí),內(nèi)部直接啟動(dòng)了 Web Server,并不會(huì)先編譯所有的代碼文件。
那僅僅是啟動(dòng) Web Server,速度上自然就快了很多。

原理:
像 Webpack 這類工具的做法是將所有模塊提前編譯、打包進(jìn) bundle 里,換句話說(shuō),不管模塊是否會(huì)被執(zhí)行,都要被編譯和打包到 bundle 里。隨著項(xiàng)目越來(lái)越大打包后的 bundle 也越來(lái)越大,打包的速度自然也就越來(lái)越慢。
Vite 利用現(xiàn)代瀏覽器原生支持 ESM 特性,省略了對(duì)模塊的打包。對(duì)于需要編譯的文件,Vite 采用的是另外一種模式:即時(shí)編譯。也就是說(shuō),只有具體去請(qǐng)求某個(gè)文件時(shí)才會(huì)編譯這個(gè)文件。所以,這種「即時(shí)編譯」的好處主要體現(xiàn)在:按需編譯。
除此之外
Vite 還提供了一個(gè)目前在幫助列表中并沒有呈現(xiàn)的一個(gè)子命令:optimize。
這個(gè)命令的作用就是單獨(dú)的去「優(yōu)化依賴」。
所謂的「優(yōu)化依賴」,指的就是自動(dòng)去把代碼中依賴的第三方模塊提前編譯出來(lái)。
例如,我們?cè)诖a中通過(guò) import 載入了 vue 這個(gè)模塊,那通過(guò)這個(gè)命令就會(huì)自動(dòng)將這個(gè)模塊打包成一個(gè)單獨(dú)的 ESM bundle, 放到 node_modules/.vite_opt_cache 目錄中。
這樣后續(xù)請(qǐng)求這個(gè)文件時(shí)就不需要再即時(shí)去加載了。
HMR
同樣也是模式的問(wèn)題,熱更新的時(shí)候,Vite 只需要立即編譯當(dāng)前所修改的文件即可,所以響應(yīng)速度非常快。
而 Webpack 修改某個(gè)文件過(guò)后,會(huì)自動(dòng)以這個(gè)文件為入口重寫 build 一次,所有的涉及到的依賴也都會(huì)被加載一遍,所以反應(yīng)速度會(huì)慢很多。
Build
Vite 在生產(chǎn)模式下打包,需要使用 vite build 命令。
這個(gè)命令內(nèi)部采用的是 Rollup 完成的應(yīng)用打包,最終還是會(huì)把文件都提前編譯并打包到一起。
對(duì)于 Code Splitting 需求,Vite 內(nèi)部采用的就是原生 Dynamic imports 特性實(shí)現(xiàn)的,所以打包結(jié)果還是只能夠支持現(xiàn)代瀏覽器。
不過(guò)好在 Dynamic imports 特性是可以有 Polyfill 的:也就是說(shuō),只要你想,它也可以運(yùn)行在相對(duì)低版本的瀏覽器中。
打包 or 不打包
隨著Vite 的出現(xiàn),引發(fā)了另外一個(gè)值得我們思考的問(wèn)題:究竟還有沒有必要打包應(yīng)用?
畢竟在它之前,我們使用 Webpack 打包應(yīng)用代碼,使之成為一個(gè) bundle.js,主要有兩個(gè)原因:
1、瀏覽器環(huán)境并不支持模塊化
2、零散的模塊文件會(huì)產(chǎn)生大量的 HTTP 請(qǐng)求
隨著瀏覽器的對(duì) ES 標(biāo)準(zhǔn)支持的逐漸完善,第一個(gè)問(wèn)題已經(jīng)慢慢不存在了。現(xiàn)階段絕大多數(shù)瀏覽器都是支持 ES Modules 的。
零散模塊文件確實(shí)會(huì)產(chǎn)生大量的 HTTP 請(qǐng)求,而大量的 HTTP 請(qǐng)求在瀏覽器端就會(huì)并發(fā)請(qǐng)求資源的問(wèn)題;
在并行請(qǐng)求時(shí),排在后面的請(qǐng)求就因?yàn)橛蛎溄訑?shù)已超過(guò)限制,而被掛起等待了一段時(shí)間。
在 HTTP 1.1 的標(biāo)準(zhǔn)下,每次請(qǐng)求都需要單獨(dú)建立 TCP 鏈接,經(jīng)過(guò)完整的通訊過(guò)程,非常耗時(shí);

而且每次請(qǐng)求除了請(qǐng)求體中的內(nèi)容,請(qǐng)求頭中也會(huì)包含很多數(shù)據(jù),大量請(qǐng)求的情況下也會(huì)浪費(fèi)很多資源。
但是這些問(wèn)題隨著 HTTP 2 的出現(xiàn),也就不復(fù)存在了。

在 HTTP/2 中,有了二進(jìn)制分幀之后,HTTP /2 不再依賴 TCP 鏈接去實(shí)現(xiàn)多流并行了,在 HTTP/2 中:
同域名下所有通信都在單個(gè)連接上完成。
單個(gè)連接可以承載任意數(shù)量的雙向數(shù)據(jù)流。
數(shù)據(jù)流以消息的形式發(fā)送,而消息又由一個(gè)或多個(gè)幀組成,多個(gè)幀之間可以亂序發(fā)送,因?yàn)楦鶕?jù)幀首部的流標(biāo)識(shí)可以重新組裝。
這一特性,使性能有了極大提升:
同個(gè)域名只需要占用一個(gè) TCP 連接,使用一個(gè)連接并行發(fā)送多個(gè)請(qǐng)求和響應(yīng),消除了因多個(gè) TCP 連接而帶來(lái)的延時(shí)和內(nèi)存消耗。
并行交錯(cuò)地發(fā)送多個(gè)請(qǐng)求,請(qǐng)求之間互不影響。
并行交錯(cuò)地發(fā)送多個(gè)響應(yīng),響應(yīng)之間互不干擾。
在 HTTP/2 中,每個(gè)請(qǐng)求都可以帶一個(gè) 31bit 的優(yōu)先值,0 表示最高優(yōu)先級(jí), 數(shù)值越大優(yōu)先級(jí)越低。有了這個(gè)優(yōu)先值,客戶端和服務(wù)器就可以在處理不同的流時(shí)采取不同的策略,以最優(yōu)的方式發(fā)送流、消息和幀。
特性小結(jié)
Vite 帶來(lái)的優(yōu)勢(shì)主要體現(xiàn)在提升開發(fā)者在開發(fā)過(guò)程中的體驗(yàn)。
1、Dev Server 無(wú)需等待,即時(shí)啟動(dòng);
2、幾乎實(shí)時(shí)的模塊熱更新;
3、所需文件按需編譯,避免編譯用不到的文件;
4、開箱即用,避免各種 Loader 和 Plugin 的配置;
TypeScript - 內(nèi)置支持
less/sass/stylus/postcss - 內(nèi)置支持(需要單獨(dú)安裝所對(duì)應(yīng)的編譯器)
Vite 的核心功能:Static Server + Compile + HMR
核心思路:
1、將當(dāng)前項(xiàng)目目錄作為靜態(tài)文件服務(wù)器的根目錄
2、攔截部分文件請(qǐng)求
3、處理代碼中 import node_modules 中的模塊
4、處理 vue 單文件組件(SFC)的編譯
5、通過(guò) WebSocket 實(shí)現(xiàn) HMR