Vite使用Esbuild提升性能詳解
前言
在上一篇 為什么有人說 vite 快,有人卻說 vite 慢? 中,我們提到過開發(fā)模式下使用 Vite
會有首屏性能下降的負(fù)面效果。之所以會造成首屏性能下降,一方面是 dev server
需要完成預(yù)構(gòu)建才可以響應(yīng)首屏請求;另一方面是需要對請求文件做實(shí)時(shí)轉(zhuǎn)換。
也許有的同學(xué)會問,是不是針對這兩個(gè)方面做優(yōu)化,就可以提升首屏性能呢?原則上這樣是沒有問題的,而且 Vite
也是這么做的。為了能提升性能,Vite
另辟蹊徑的借助了 Esbuild
能快速完成項(xiàng)目打包、文件轉(zhuǎn)換的能力來進(jìn)行預(yù)構(gòu)建、內(nèi)容轉(zhuǎn)換,效果非常好。
今天小編就通過本文和大家一起聊一聊 Vite
是怎樣利用 Esbuild
來提升性能的。
初探 Esbuild
首先,小編先帶大家簡單了解一下 Esbuild
,其官方地址是: Esbuild。
什么是 Esbuild
Esbuild
是一款基于 Go
語言開發(fā)的 javascript
打包工具,最大的一個(gè)特征就是快。
通過官網(wǎng)提供的一張圖,我們可以清晰的看到 Esbuild
的表現(xiàn)是多么優(yōu)秀:
同樣規(guī)模的項(xiàng)目,使用 Esbuild
可以將打包速度提升 10
- 100
倍,這對廣大一直飽受 Webpack
緩慢打包速度折磨的開發(fā)人員來說,簡直就是福音。
而 Esbuild
之所以能這么快,主要原因有兩個(gè):
Go
語言開發(fā),可以多線程打包,代碼直接編譯成機(jī)器碼;Webpack
一直被人詬病構(gòu)建速度慢,主要原因是在打包構(gòu)建過程中,存在大量的resolve
、load
、transform
、parse
操作(詳見 為什么有人說 vite 快,有人卻說 vite 慢?- 快速的冷啟動 ),而這些操作通常是通過javascript
代碼來執(zhí)行的。要知道,javascript
并不是什么高效的語言,在執(zhí)行過程中要先編譯后執(zhí)行,還是單線程并且不能利用多核cpu
優(yōu)勢,和Go
語言相比,效率很低。- 可充分利用多核
cpu
優(yōu)勢;
關(guān)鍵 API - transfrom & build
Esbuild
并不復(fù)雜。它對外提供了兩個(gè) API
- transform
和 build
,使用起來非常簡單。
transfrom
,轉(zhuǎn)換的意思。通過這個(gè) api,我們可以將 ts
、jsx
、tsx
等格式的內(nèi)容轉(zhuǎn)化為 js
。 transfrom
只負(fù)責(zé)文件內(nèi)容轉(zhuǎn)換,并不會生成一個(gè)新的文件。
build
,構(gòu)建的意思,根據(jù)指定的單個(gè)或者多個(gè)入口,分析依賴,并使用 loader
將不同格式的內(nèi)容轉(zhuǎn)化為 js 內(nèi)容,生成一個(gè)或多個(gè) bundle
文件。
這兩個(gè) API
的使用方式:
const res = await esbuild.transform(code, options) // 將 code 轉(zhuǎn)換為指定格式的內(nèi)容 esbuild.build(options) // 打包構(gòu)建
關(guān)于使用 transform
、build
需要傳入的具體配置項(xiàng),本文就不詳細(xì)說明了,官網(wǎng)對這一塊兒有很詳細(xì)的說明,感興趣的同學(xué)可以去官網(wǎng) - simple-options、Advanced options 看看,也可以自己動手試試。
plugin
和 Webpack
、Rollup
等構(gòu)建工具一樣,Esbuild
也提供了供外部使用的 plugin
,使得我們可以介入構(gòu)建打包過程。
在這里要說明一點(diǎn),只有 build
這個(gè) API
的入?yún)⒅锌梢耘渲?plugin
,transform
不可以。
一個(gè)標(biāo)準(zhǔn)的 plugin
的標(biāo)準(zhǔn)格式如下:
let customerPlugin = { name: 'xxx', setup: (build) => { build.onResolve({ filter: '', namespace: '' }, args => { ...}); build.onLoad({ filter: '', namespace: ''}, args => { ... }); build.onStart(() => { ... }); build.onEnd((result) => { ... }); } }
其中,setup
可以幫助我們在 build
的各個(gè)過程中注冊 hook
。
Esbuild
對外提供的 hook
比較簡單,總共 4
個(gè):
onResolve
, 解析url
時(shí)觸發(fā),可自定義url
如何解析。如果callback
有返回path
,后面的同類型callback
將不會執(zhí)行。所有的onResolve
callback
將按照對應(yīng)的plugin
注冊的順序執(zhí)行。onLoad
, 加載模塊時(shí)觸發(fā),可自定義模塊如何加載。 如果callback
有返回contents
,后面的同類型callback
將不會執(zhí)行。所有的onLoad
callback
將按照對應(yīng)的plugin
注冊的順序執(zhí)行。onStart
, 每次build
開始時(shí)都會觸發(fā),沒有入?yún)ⅲ虼瞬痪哂懈淖?build
的能力。多個(gè)plugin
的onStart
并行執(zhí)行。onEnd
, 每次build
結(jié)束時(shí)會觸發(fā),入?yún)?build
的結(jié)果,可對result
做修改。所有的的onEnd
將按照對應(yīng)的plugin
注冊的順序執(zhí)行。
正是有了 onResolve
、onLoad
、onStart
、onEnd
,我們可以在 build
過程中的解析 url
、加載模塊內(nèi)容、構(gòu)建開始、構(gòu)建結(jié)束階段介入,做自定義操作。
Esbuild 在 Vite 中的巧妙使用
了解了 Esbuild
的基本用法以后,小編就帶大家一起來看看 Vite
是怎么利用 Esbuild
來做預(yù)構(gòu)建和內(nèi)容轉(zhuǎn)換的。
預(yù)構(gòu)建
先來回顧一下為什么要做預(yù)構(gòu)建。
原因有兩點(diǎn):
- 將非
ESM
規(guī)范的代碼轉(zhuǎn)換為符合ESM
規(guī)范的代碼; - 將第三方依賴內(nèi)部的多個(gè)文件合并為一個(gè),減少
http
請求數(shù)量;
要完成預(yù)構(gòu)建,最關(guān)鍵的兩點(diǎn)是找到項(xiàng)目中所有的第三份依賴和對第三方依賴做合并、轉(zhuǎn)換。借助 Esbuild
,Vite
很輕松的實(shí)現(xiàn)了這兩個(gè)訴求。
尋找第三方依賴
尋找第三方依賴的過程非常簡單,分為兩步:
定義一個(gè)帶 onResolve hook
和 onLoad hook
的 esbuild plugin
;
執(zhí)行 esbuild
的 build
方法做打包構(gòu)建;
和 Webpack
、Rollup
、Parcel
等構(gòu)建工具一樣,Esbuild
在做打包構(gòu)建時(shí)也要構(gòu)建模塊依賴圖 - module graph
(具體過程可參考 為什么有人說 vite 快,有人卻說 vite 慢?- 快速的冷啟動 中 Webpack
構(gòu)建 module graph
)。
在構(gòu)建 module graph
時(shí),第一步就是解析模塊的絕對路徑,這個(gè)時(shí)候就會觸發(fā) onResolve hook
。在 onResolve hook
觸發(fā)時(shí),會傳入模塊的路徑。根據(jù)模塊的路徑,我們就可以判斷出這個(gè)模塊是第三方依賴還是業(yè)務(wù)代碼。
舉個(gè) ??,
// main.tsx import react from 'react'; import CustomeComponent from './components/CustomeComponent'; ...
在對 main.tsx
的內(nèi)容做 parser
操作時(shí),能知道 main.tsx
依賴 react
和 CustomeComponent
,然后開始解析 react
和 CustomeComponent
。
解析 react
、CustomeComponent
時(shí),會觸發(fā) onResolve hook
,入?yún)⒎謩e為 'react'
和 './components/CustomeComponent'。
根據(jù)入?yún)?,我們可以很清楚的區(qū)分 'react'
是第三方依賴,'./components/CustomeComponet' 是業(yè)務(wù)代碼。
這樣,esbuild
完成構(gòu)建,項(xiàng)目中的第三方依賴也就收集完畢了。所有的第三方依賴會收集到一個(gè) deps
列表中。
- 合并、轉(zhuǎn)換第三方依賴
- 知道了項(xiàng)目中的第三方依賴以后,再做合并、轉(zhuǎn)換操作就非常簡單了。
- 這一步,
Vite
直接通過esbuild
提供的build
方法,指定entryPoints
為收集到的第三方依賴,format
為esm
,再做一次打包構(gòu)建。 - 這一次,會對第三方依賴做合并、轉(zhuǎn)換操作。打包構(gòu)建完成以后,再把構(gòu)建內(nèi)容輸出到 /node_modules/.vite/deps 下。
這樣,通過兩次 esbuild.build
,預(yù)構(gòu)建就完成了。
middlewares 中內(nèi)容轉(zhuǎn)換
Vite
中源文件的轉(zhuǎn)換是在 dev server
啟動以后通過 middlewares
實(shí)現(xiàn)的。
當(dāng)瀏覽器發(fā)起請求以后,dev sever
會通過相應(yīng)的 middlewares
對請求做處理,然后將處理以后的內(nèi)容返回給瀏覽器。
middlewares
對源文件的處理,分為 resolve
、load
、transform
、parser
四個(gè)過程:
resolve
- 解析url
,找到源文件的絕對路徑;load
- 加載源文件。如果是第三方依賴,直接將預(yù)構(gòu)建內(nèi)容返回給瀏覽器;如果是業(yè)務(wù)代碼,繼續(xù)transform
、parser
。transfrom
- 對源文件內(nèi)容做轉(zhuǎn)換,即ts
->js
,less
->css
等。轉(zhuǎn)換完成的內(nèi)容可以直接返回給瀏覽器了。parser
- 對轉(zhuǎn)換以后的內(nèi)容做分析,找到依賴模塊,對依賴模塊做預(yù)轉(zhuǎn)換 -pre transform
操作,即重復(fù)1
-4
。pre transform
是Vite
做的一個(gè)優(yōu)化點(diǎn)。預(yù)轉(zhuǎn)換的內(nèi)容會先做緩存,等瀏覽器發(fā)起請求以后,如果已經(jīng)完成轉(zhuǎn)換,直接將緩存的內(nèi)容返回給瀏覽器。
Vite
在處理步驟 3
時(shí),是通過 esbuild.transform
實(shí)現(xiàn)的,對比 Webpack
使用各個(gè) loader
處理源文件,那是非常簡單、快捷的。
結(jié)束語
有一說一,Vite
通過 Esbuild
來優(yōu)化預(yù)構(gòu)建和內(nèi)容轉(zhuǎn)換的思路非常棒,這給我們以后處理同類問題提供了解決方案,真心給尤大點(diǎn) ????。
另外除了使用 Esbuild
, Vite
內(nèi)部還有很多可以拿出來單獨(dú)講的優(yōu)化技巧,這個(gè)以后有機(jī)會小編可以再給大家詳細(xì)講講。
以上就是Vite使用Esbuild提升性能詳解的詳細(xì)內(nèi)容,更多關(guān)于Vite Esbuild性能提升的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue項(xiàng)目多環(huán)境配置(.env)的實(shí)現(xiàn)
最常見的多環(huán)境配置,就是開發(fā)環(huán)境配置,和生產(chǎn)環(huán)境配置,本文主要介紹了vue項(xiàng)目多環(huán)境配置的實(shí)現(xiàn),感興趣的可以了解一下2021-07-07vue-draggable實(shí)現(xiàn)拖拽表單的示例代碼
本文主要介紹了vue-draggable實(shí)現(xiàn)拖拽表單的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05vue中數(shù)據(jù)不響應(yīng)的問題及解決
這篇文章主要介紹了vue中數(shù)據(jù)不響應(yīng)的問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09詳解三種方式解決vue中v-html元素中標(biāo)簽樣式
這篇文章主要介紹了三種方式解決vue中v-html元素中標(biāo)簽樣式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11vue $attrs和$listeners的使用與區(qū)別
本文主要介紹了vue $attrs和$listeners的使用與區(qū)別,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02使用Electron打包vue文件變成exe應(yīng)用程序的全過程
這篇文章主要給大家介紹了使用Electron打包vue文件變成exe應(yīng)用程序的全過程,文中通過代碼示例和圖文結(jié)合的方式給大家講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-01-01Vue3+Spring Framework框架開發(fā)實(shí)戰(zhàn)
這篇文章主要為大家介紹了Vue3+Spring Framework框架開發(fā)實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04vue.config.js配置proxy代理產(chǎn)生404錯(cuò)誤的原因及解決
這篇文章主要介紹了vue.config.js配置proxy代理產(chǎn)生404錯(cuò)誤的原因及解決,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06