前言
前面我們有提到Vite在開發階段,提倡的是一個no-bundle的理念,不必與webpack那樣需要先將整個項目進行打包構建。但是no-bundle的理念只適合源代碼部分(我們自己寫的代碼),vite會將項目中的所有模塊分為依賴與源碼兩部分。
依賴: 指的是一些不會變動的一些模塊,如:node_modules中的第三方依賴,這部分代碼vite會在啓動本地服務之前使用esbuild進行預構建。esbuild 使用 Go 編寫,比使用 JavaScript 編寫的打包器預構建依賴快 10-100 倍。
源碼: 指的是我們自己開發時寫的那部分代碼,這部分代碼可能會經常變動,並且一般不會同時加載所有源代碼。
所以總結來説:no-bundle是針對源碼的,而預構建是針對第三方依賴的
使用預構建的原因
主要有以下兩點:
- commonJS 與 UMD兼容:因為
Vite在開發階段主要是依賴瀏覽器原生ES模塊化規範,所以無論是我們的源代碼還是第三方依賴都得符合ESM的規範,但是目前並不是所有第三方依賴都有ESM的版本,所以需要對第三方依賴進行預編譯,將它們轉換成EMS規範的產物。
比如React,它就沒有ESM的版本,所以在使用Vite時需要預構建
- 性能: 為了提高後續頁面的加載性能,Vite將那些具有許多內部模塊的 ESM 依賴項轉換為單個模塊。
比如常用的loads-es
我們引入lodash-es工具包中的debounce方法,此時它理想狀態應該是隻發出一個請求
import { debounce } from 'lodash-es'
事實也是這樣
但這是預構建的功勞,如果我們對lodash-es關閉預構建呢?
vite配置文件加上如下代碼,再來試試:
// vite.config.js
optimizeDeps: {
exclude: ['lodash-es']
}
可以看到,此時發起了600多個請求,這是因為lodash-es 有超過 600 個內置模塊!
vite通過將 lodash-es 預構建成單個模塊,只需要發起一個HTTP請求!可以很大程度地提高加載性能
由於Vite的預構建是基於性能優異的Esbuild來完成的,所以並不會造成明顯的打包性能問題
開啓預構建
默認配置
一般來説,Vite幫我們默認開啓了預構建
預構建產物會存放在:node_modules/.vite/deps
裏面會有一個_metadata.json的文件,這裏保存着已經預構建過的依賴信息
對於預構建產物的請求,Vite會設置為強緩存,有效時間為1年,對於有效期內的請求,會直接使用緩存內容
如果只有HTTP強緩存肯定也不行,如果用户更新了依賴版本,在緩存過期之前,瀏覽器拿到的一直是舊版本的內容。
所以Vite對本地文件也設置了緩存判斷,如果下面幾個地方任意一個地方有變動,Vite將會對依賴進行重新預構建:
- 項目依賴
dependencies變更
- 各種包管理器的
lock文件變更
optimizeDeps配置內容變更
自定義配置
entries
默認情況下,Vite會抓取項目中的index.html來檢測需要預構建的依賴
optimizeDeps: {
entries: ['index.html']
}
如果指定了 build.rollupOptions.input,Vite 將轉而去抓取這些入口點。
exclude
排除需要預構建的依賴項
optimizeDeps: {
exclude: ['lodash-es']
}
include
默認情況下,不在 node_modules 中的依賴不會被預構建。使用此選項可強制選擇預構建的依賴項。
optimizeDeps: {
include: ['lodash-es']
}
預構建流程
還是從源碼入手,在啓動服務的過程中會執行一個initDepsOptimizer表示初始化依賴優化
接着找到定義initDepsOptimizer方法的地方
在這裏會執行createDepsOptimizer方法,再接着找到定義createDepsOptimizer的地方
這裏首先會去執行loadCachedDepOptimizationMetadata用於獲取本地緩存中的metadata數據
該函數會在獲取到_metadata.json文件內容之後去對比lock文件hash以及配置文件optimizeDeps內容,如果一樣説明預構建緩存沒有任何改變,無需重新預構建,直接使用上次預構建緩存即可
如果沒有緩存時則需要進行依賴掃描
這裏主要是會調用scanImport方法,從名字也能看出該方法應該是通過掃描項目中的import語句來得到需要預編譯的依賴
最終會返回一個prepareEsbuildScanner方法
最後該方法中會使用esbuild對掃描出來的依賴項進行預編譯。