Vite使用Esbuild提升性能詳解
前言
在上一篇 為什么有人說(shuō) vite 快,有人卻說(shuō) vite 慢? 中,我們提到過(guò)開(kāi)發(fā)模式下使用 Vite 會(huì)有首屏性能下降的負(fù)面效果。之所以會(huì)造成首屏性能下降,一方面是 dev server 需要完成預(yù)構(gòu)建才可以響應(yīng)首屏請(qǐng)求;另一方面是需要對(duì)請(qǐng)求文件做實(shí)時(shí)轉(zhuǎn)換。
也許有的同學(xué)會(huì)問(wèn),是不是針對(duì)這兩個(gè)方面做優(yōu)化,就可以提升首屏性能呢?原則上這樣是沒(méi)有問(wèn)題的,而且 Vite 也是這么做的。為了能提升性能,Vite 另辟蹊徑的借助了 Esbuild 能快速完成項(xiàng)目打包、文件轉(zhuǎn)換的能力來(lái)進(jìn)行預(yù)構(gòu)建、內(nèi)容轉(zhuǎn)換,效果非常好。
今天小編就通過(guò)本文和大家一起聊一聊 Vite 是怎樣利用 Esbuild 來(lái)提升性能的。
初探 Esbuild
首先,小編先帶大家簡(jiǎn)單了解一下 Esbuild,其官方地址是: Esbuild。
什么是 Esbuild
Esbuild 是一款基于 Go 語(yǔ)言開(kāi)發(fā)的 javascript 打包工具,最大的一個(gè)特征就是快。
通過(guò)官網(wǎng)提供的一張圖,我們可以清晰的看到 Esbuild 的表現(xiàn)是多么優(yōu)秀:

同樣規(guī)模的項(xiàng)目,使用 Esbuild 可以將打包速度提升 10 - 100 倍,這對(duì)廣大一直飽受 Webpack 緩慢打包速度折磨的開(kāi)發(fā)人員來(lái)說(shuō),簡(jiǎn)直就是福音。
而 Esbuild 之所以能這么快,主要原因有兩個(gè):
Go語(yǔ)言開(kāi)發(fā),可以多線程打包,代碼直接編譯成機(jī)器碼;Webpack一直被人詬病構(gòu)建速度慢,主要原因是在打包構(gòu)建過(guò)程中,存在大量的resolve、load、transform、parse操作(詳見(jiàn) 為什么有人說(shuō) vite 快,有人卻說(shuō) vite 慢?- 快速的冷啟動(dòng) ),而這些操作通常是通過(guò)javascript代碼來(lái)執(zhí)行的。要知道,javascript并不是什么高效的語(yǔ)言,在執(zhí)行過(guò)程中要先編譯后執(zhí)行,還是單線程并且不能利用多核cpu優(yōu)勢(shì),和Go語(yǔ)言相比,效率很低。- 可充分利用多核
cpu優(yōu)勢(shì);
關(guān)鍵 API - transfrom & build
Esbuild 并不復(fù)雜。它對(duì)外提供了兩個(gè) API - transform 和 build,使用起來(lái)非常簡(jiǎn)單。
transfrom,轉(zhuǎn)換的意思。通過(guò)這個(gè) api,我們可以將 ts、jsx、tsx 等格式的內(nèi)容轉(zhuǎn)化為 js。 transfrom 只負(fù)責(zé)文件內(nèi)容轉(zhuǎn)換,并不會(huì)生成一個(gè)新的文件。
build,構(gòu)建的意思,根據(jù)指定的單個(gè)或者多個(gè)入口,分析依賴(lài),并使用 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ì)說(shuō)明了,官網(wǎng)對(duì)這一塊兒有很詳細(xì)的說(shuō)明,感興趣的同學(xué)可以去官網(wǎng) - simple-options、Advanced options 看看,也可以自己動(dòng)手試試。
plugin
和 Webpack、Rollup 等構(gòu)建工具一樣,Esbuild 也提供了供外部使用的 plugin,使得我們可以介入構(gòu)建打包過(guò)程。
在這里要說(shuō)明一點(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 可以幫助我們?cè)?build 的各個(gè)過(guò)程中注冊(cè) hook。
Esbuild 對(duì)外提供的 hook 比較簡(jiǎn)單,總共 4 個(gè):
onResolve, 解析url時(shí)觸發(fā),可自定義url如何解析。如果callback有返回path,后面的同類(lèi)型callback將不會(huì)執(zhí)行。所有的onResolvecallback將按照對(duì)應(yīng)的plugin注冊(cè)的順序執(zhí)行。onLoad, 加載模塊時(shí)觸發(fā),可自定義模塊如何加載。 如果callback有返回contents,后面的同類(lèi)型callback將不會(huì)執(zhí)行。所有的onLoadcallback將按照對(duì)應(yīng)的plugin注冊(cè)的順序執(zhí)行。onStart, 每次build開(kāi)始時(shí)都會(huì)觸發(fā),沒(méi)有入?yún)?,因此不具有改?build的能力。多個(gè)plugin的onStart并行執(zhí)行。onEnd, 每次build結(jié)束時(shí)會(huì)觸發(fā),入?yún)?build的結(jié)果,可對(duì)result做修改。所有的的onEnd將按照對(duì)應(yīng)的plugin注冊(cè)的順序執(zhí)行。
正是有了 onResolve、onLoad、onStart、onEnd,我們可以在 build 過(guò)程中的解析 url、加載模塊內(nèi)容、構(gòu)建開(kāi)始、構(gòu)建結(jié)束階段介入,做自定義操作。
Esbuild 在 Vite 中的巧妙使用
了解了 Esbuild 的基本用法以后,小編就帶大家一起來(lái)看看 Vite 是怎么利用 Esbuild 來(lái)做預(yù)構(gòu)建和內(nèi)容轉(zhuǎn)換的。
預(yù)構(gòu)建
先來(lái)回顧一下為什么要做預(yù)構(gòu)建。
原因有兩點(diǎn):
- 將非
ESM規(guī)范的代碼轉(zhuǎn)換為符合ESM規(guī)范的代碼; - 將第三方依賴(lài)內(nèi)部的多個(gè)文件合并為一個(gè),減少
http請(qǐng)求數(shù)量;
要完成預(yù)構(gòu)建,最關(guān)鍵的兩點(diǎn)是找到項(xiàng)目中所有的第三份依賴(lài)和對(duì)第三方依賴(lài)做合并、轉(zhuǎn)換。借助 Esbuild,Vite 很輕松的實(shí)現(xiàn)了這兩個(gè)訴求。
尋找第三方依賴(lài)
尋找第三方依賴(lài)的過(guò)程非常簡(jiǎn)單,分為兩步:
定義一個(gè)帶 onResolve hook 和 onLoad hook 的 esbuild plugin;
執(zhí)行 esbuild 的 build 方法做打包構(gòu)建;
和 Webpack、Rollup、Parcel 等構(gòu)建工具一樣,Esbuild 在做打包構(gòu)建時(shí)也要構(gòu)建模塊依賴(lài)圖 - module graph(具體過(guò)程可參考 為什么有人說(shuō) vite 快,有人卻說(shuō) vite 慢?- 快速的冷啟動(dòng) 中 Webpack 構(gòu)建 module graph)。
在構(gòu)建 module graph 時(shí),第一步就是解析模塊的絕對(duì)路徑,這個(gè)時(shí)候就會(huì)觸發(fā) onResolve hook。在 onResolve hook 觸發(fā)時(shí),會(huì)傳入模塊的路徑。根據(jù)模塊的路徑,我們就可以判斷出這個(gè)模塊是第三方依賴(lài)還是業(yè)務(wù)代碼。
舉個(gè) ??,
// main.tsx import react from 'react'; import CustomeComponent from './components/CustomeComponent'; ...
在對(duì) main.tsx 的內(nèi)容做 parser 操作時(shí),能知道 main.tsx 依賴(lài) react 和 CustomeComponent,然后開(kāi)始解析 react 和 CustomeComponent。
解析 react、CustomeComponent 時(shí),會(huì)觸發(fā) onResolve hook,入?yún)⒎謩e為 'react' 和 './components/CustomeComponent'。
根據(jù)入?yún)?,我們可以很清楚的區(qū)分 'react' 是第三方依賴(lài),'./components/CustomeComponet' 是業(yè)務(wù)代碼。
這樣,esbuild 完成構(gòu)建,項(xiàng)目中的第三方依賴(lài)也就收集完畢了。所有的第三方依賴(lài)會(huì)收集到一個(gè) deps 列表中。
- 合并、轉(zhuǎn)換第三方依賴(lài)
- 知道了項(xiàng)目中的第三方依賴(lài)以后,再做合并、轉(zhuǎn)換操作就非常簡(jiǎn)單了。
- 這一步,
Vite直接通過(guò)esbuild提供的build方法,指定entryPoints為收集到的第三方依賴(lài),format為esm,再做一次打包構(gòu)建。 - 這一次,會(huì)對(duì)第三方依賴(lài)做合并、轉(zhuǎn)換操作。打包構(gòu)建完成以后,再把構(gòu)建內(nèi)容輸出到 /node_modules/.vite/deps 下。
這樣,通過(guò)兩次 esbuild.build,預(yù)構(gòu)建就完成了。
middlewares 中內(nèi)容轉(zhuǎn)換
Vite 中源文件的轉(zhuǎn)換是在 dev server 啟動(dòng)以后通過(guò) middlewares 實(shí)現(xiàn)的。
當(dāng)瀏覽器發(fā)起請(qǐng)求以后,dev sever 會(huì)通過(guò)相應(yīng)的 middlewares 對(duì)請(qǐng)求做處理,然后將處理以后的內(nèi)容返回給瀏覽器。
middlewares 對(duì)源文件的處理,分為 resolve、load、transform、parser 四個(gè)過(guò)程:
resolve- 解析url,找到源文件的絕對(duì)路徑;load- 加載源文件。如果是第三方依賴(lài),直接將預(yù)構(gòu)建內(nèi)容返回給瀏覽器;如果是業(yè)務(wù)代碼,繼續(xù)transform、parser。transfrom- 對(duì)源文件內(nèi)容做轉(zhuǎn)換,即ts->js,less->css等。轉(zhuǎn)換完成的內(nèi)容可以直接返回給瀏覽器了。parser- 對(duì)轉(zhuǎn)換以后的內(nèi)容做分析,找到依賴(lài)模塊,對(duì)依賴(lài)模塊做預(yù)轉(zhuǎn)換 -pre transform操作,即重復(fù)1-4。pre transform是Vite做的一個(gè)優(yōu)化點(diǎn)。預(yù)轉(zhuǎn)換的內(nèi)容會(huì)先做緩存,等瀏覽器發(fā)起請(qǐng)求以后,如果已經(jīng)完成轉(zhuǎn)換,直接將緩存的內(nèi)容返回給瀏覽器。
Vite 在處理步驟 3 時(shí),是通過(guò) esbuild.transform 實(shí)現(xiàn)的,對(duì)比 Webpack 使用各個(gè) loader 處理源文件,那是非常簡(jiǎn)單、快捷的。
結(jié)束語(yǔ)
有一說(shuō)一,Vite 通過(guò) Esbuild 來(lái)優(yōu)化預(yù)構(gòu)建和內(nèi)容轉(zhuǎn)換的思路非常棒,這給我們以后處理同類(lèi)問(wèn)題提供了解決方案,真心給尤大點(diǎn) ????。
另外除了使用 Esbuild, Vite 內(nèi)部還有很多可以拿出來(lái)單獨(dú)講的優(yōu)化技巧,這個(gè)以后有機(jī)會(huì)小編可以再給大家詳細(xì)講講。
以上就是Vite使用Esbuild提升性能詳解的詳細(xì)內(nèi)容,更多關(guān)于Vite Esbuild性能提升的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
通過(guò)debug搞清楚.vue文件如何變成.js文件(案例詳解)
這篇文章主要介紹了通過(guò)debug搞清楚.vue文件如何變成.js文件,本文以@vitejs/plugin-vue舉例,通過(guò)debug的方式帶你一步一步的搞清楚vue文件是如何編譯為js文件的,需要的朋友可以參考下2024-07-07
使用axios請(qǐng)求時(shí),發(fā)送formData請(qǐng)求的示例
今天小編就為大家分享一篇使用axios請(qǐng)求時(shí),發(fā)送formData請(qǐng)求的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-10-10
uniApp前端實(shí)現(xiàn)文字識(shí)別并提取詳細(xì)分析
這篇文章主要介紹了如何實(shí)現(xiàn)文字圖片分析的功能,包括頁(yè)面結(jié)構(gòu)、樣式和腳本邏輯,主要步驟包括獲取access_token、將圖片轉(zhuǎn)換為Base64格式、上傳至百度文字識(shí)別接口并處理返回結(jié)果,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-02-02
在Vue3項(xiàng)目中使用Jest配置生成測(cè)試報(bào)告的示例詳解
在Vue 3項(xiàng)目中使用Jest進(jìn)行單元測(cè)試是一種常見(jiàn)的做法,它可以幫助我們驗(yàn)證代碼的正確性和穩(wěn)定性,本文將介紹如何在Vue 3項(xiàng)目中配置Jest,以生成測(cè)試報(bào)告,需要的朋友可以參考下2023-09-09
Vue監(jiān)聽(tīng)數(shù)組變化源碼解析
這篇文章主要為大家詳細(xì)解析了Vue監(jiān)聽(tīng)數(shù)組變化的源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
vue-quill-editor富文本編輯器上傳視頻功能詳解
需求需要實(shí)現(xiàn)富文本的功能,同時(shí)富文本中還可以上傳視頻和圖片,選來(lái)選去最后決定了用這個(gè)富文本編輯器,下面這篇文章主要給大家介紹了關(guān)于vue-quill-editor富文本編輯器上傳視頻功能的相關(guān)資料,需要的朋友可以參考下2023-05-05
Vue純前端使用exceljs導(dǎo)出excel文件的完整圖文教程
這篇文章將一步一步為大家詳細(xì)介紹一下exceljs插件中的使用,以及如何使用exceljs導(dǎo)出excel文件,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03

