前端構建工具vite進階系列(二) -- vite的依賴預構建與配置文件相關處理

語言: CN / TW / HK

theme: devui-blue highlight: solarized-light


前言

上一章前端構建工具vite進階系列(一) -- vite比webpack的優勢與開箱即用中,我們稍微體驗了一下使用vite來打包資源,並且可以解決非相對/絕對路徑的資源引入導致的報錯問題,那麼這一章我們來講講vite的本身處理。vite之所以能夠比webpack快幾十倍,依賴預構建起到了很大的作用,那麼這一章講講依賴預構建與環境變量相關的事情吧。

為什麼要依賴預構建

  1. CommonJS 和 UMD 兼容性:  開發階段中,Vite 的開發服務器將所有代碼視為原生 ES 模塊。因此,Vite 必須先將作為 CommonJSUMD 發佈的依賴項轉換為 ESM

    當轉換 CommonJS 依賴時,Vite 會執行智能導入分析,這樣即使導出是動態分配的(如 React),按名導入也會符合預期效果:

    js // 符合預期 import React, { useState } from 'react'

  2. 性能:  Vite 將有許多內部模塊的 ESM 依賴關係轉換為單個模塊,以提高後續頁面加載性能。

    一些包將它們的 ES 模塊構建作為許多單獨的文件相互導入。例如,lodash-es 有超過 600 個內置模塊!當我們執行 import { debounce } from 'lodash-es' 時,瀏覽器同時發出 600 多個 HTTP 請求!儘管服務器在處理這些請求時沒有問題,但大量的請求會在瀏覽器端造成網絡擁塞,導致頁面的加載速度相當慢。

    通過預構建 lodash-es 成為一個模塊,我們就只需要一個 HTTP 請求了!

    vite是怎麼解決路徑問題的

    vite啟動的時候,會執行bin目錄下的vite.js文件,在這個文件裏面我們會看到獲取了當前的電腦的絕對路徑,如果不包含node_module路徑,就需要引入source-map-support這個包來處理,如果是相對路徑則會進行路徑補全,當然在dev環境下都會有node_module路徑,在prod環境下,vite會使用rollup來進行打包。

image.png 進行路徑補全當然可以解決這個問題,但是vite一想,我既然是基於ESM規範的,那麼如果用户使用了CommonJS規範的導出包,那我還不是需要編譯成ESM嗎?所以vite就藉助了esbuild庫(esbuild是一個對js語法處理的庫),對用户使用的CommonJS規範的庫進行了轉換成ESM,並且輸入在了node_module/.vite/deps目錄下。 - 一來可以統一ESM模塊,統一模塊規範。 - 二來可以解決路徑問題,方便路徑重寫。 - 三來可以優化http多包傳輸的性能問題。

為什麼原生ESM不支持node_module?:因為如果支持的話,那將會帶來非常大的網絡性能問題,對於ESM裏面具有其他依賴的ESM來説,那瀏覽器將會無限制的請求依賴庫,舉個例子當我們使用lodash-es庫的時候,我們把不進行預構建的模塊寫在vite.config.json裏面。 js export default { optimizeDeps:{ exclude:["lodash-es"] // 當遇到lodash-es的時候 不進行預構建 } } 那麼就會產生如下多的數不清的文件。

image.png

所以vite能夠把ESM庫中的所有三方依賴全部集成,只生成一個或者多個文件(多個文件但是不是類似於上面這麼多。。。)這才能夠説明可以用來優化http多包傳輸的性能問題,所以這上面也是vite的依賴預構建做的事情。 - esbuild庫 : https://esbuild.github.io/

配置文件相關處理

  1. 語法提示的支持
    • 類型註解 /** @type import('vite').UserConfig*/

image.png - defineConfig

image.png

  1. 環境配置文件處理 ```js import {defineConfig} from 'vite'; import viteBaseConfig from './vite-base-config.js' import viteDevConfig from './vite-dev-config.js' import viteProdConfig from './vite-prod-config.js'

const handleEnv = { "build":()=>{ // build的時候做的事情 return Object.assign({}, viteProdConfig, viteBaseConfig) }, "serve":()=>{ // serve的時候做的事情 return Object.assign({}, viteDevConfig, viteBaseConfig) } } export default defineConfig(({command})=>{ console.log(handleEnvcommand) return handleEnvcommand }) `` 3. 環境變量處理 -vite對環境變量的處理是藉助於第三方庫dotenv實現的,執行命令的時候,dotenv會去讀取.env文件,然後注入到process對象當中。當然用户配置大於默認,我們可以在vite.config.js裏面配置envDir去**指定環境變量的文件地址**。 - 為什麼nodejs可以讀取ESM規範的vite.config.js呢? -vite在讀取vite.config.js文件的時候,如果遇到ESM規範,node會去解析成CommonJS規範。 - 如何解析:讀取文件的結果就是字符串,通過替換importrequire`的方式來進行規範轉換。

  • 通過命令來獲取環境參數 js export default defineConfig(({command, mode})=>{ // mode 為執行指令環境 // npm run serve => mode:development // npm run build => mode:production console.log(handleEnv[command]()) return handleEnv[command]() }) 通過上述代碼中的mode,我們知道了只要我們敲命令,mode會自動獲取到,所以我們可以手動設置.env,vite給我們提供了一個loadEnv方法來獲取環境變量。 ```js export default defineConfig(({command, mode})=>{ // mode 為執行指令環境 // npm run serve => mode:development // npm run build => mode:production

const env = loadEnv(mode, process.cwd(), '') console.log('/////env', env) console.log(handleEnvcommand) return handleEnvcommand }) 我們新建`.env.production`和`.env.development`文件,分別注入`ENV = 10011`和`ENV = 10010`js // .env.production ENV = 10011 BASE_URL = 'http://dev.api'

// .env.development ENV = 10010 BASE_URL = 'https://api' `` 所以npm run serve` 的結果是:

image.png 所以 npm run build 的結果是:

image.png

  • import.meta.env 這個是官方提供的一個變量對象,他能夠有效的讀取到當前環境變量,我們先來看一看官網。

image.png 可以在代碼中獲取到import.meta.env的內容,如果要想獲取到環境變量,則需要命名為VITE_*,為前綴的值,否則vite訪問不到,原因是因為vite做了一層攔截,把沒有帶VITE前綴的變量,不會注入到import.meta.env中。

image.png 當然這是默認的,一切要遵循配置大於默認,所以我們可以在vite.config.js中去定製envPrefix:"ENV_",才可以。

image.png

  • 多種環境配置
    • 在實際項目中肯定會有測試環境預發佈環境等,那這裏我們就按照prepare來指定一下預發佈環境。
    • .env.prepare文件。 js ENV_PREPARE = 10012 ENV_BASE_URL = https://prepare.api/ image.png

總結

本文主要講解了vite的預構建與config文件、環境變量的處理,我們看到了其實跟webpack很類似,在這裏我們也預留了問題,dotenv是怎麼去處理環境變量的?ESM是怎麼去變成CommonJS的,這些問題後期源碼閲讀的時候,都會得到解答,請戳 >>> 前端構建工具vite進階系列(三) -- 靜態資源與css模塊化的處理