您好,登錄后才能下訂單哦!
這篇文章主要介紹“vite是如何解析.env文件的”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“vite是如何解析.env文件的”文章能幫助大家解決問題。
使用vite構建的vue3項目中,可以在根目錄下創建.env.[模式]文件定義一種或多種模式,并且在這個文件中定義的變量就是此模式的環境變量。定義了環境變量之后就可以通過import.meta.env.[變量名]的方式讀取環境變量了。
那么我們要來思考兩個問題:第一,vite如何讀取.env文件中定義的配置;第二,vite如何將.env文件中配置的環境變量掛載到import.meta.env環境變量上的。
1.1 問題的著眼點是什么?
首先我們看一下vite如何讀取.env文件中定義的配置。如下圖所示,在項目根目錄下有.env.development,.env.fat,.env.uat,.env.pro四個模式文件,其中development模式是對應默認的開發環境用于本地開發,fat模式對應的也是開發環境用于自測,uat模式對應的是預發布環境用于測試團隊測試,pro模式對應的是生產環境也叫線上環境用于客戶使用。
那么如何能夠讓vite知道我們要使用相關模式的文件呢?運行vite 或者vite build命令時可以通過--mode或者-m設置環境模式(詳見文檔),如下圖所示:
這就提示我們要了解vite如何讀取定義環境變量的模式文件則需要從vite命令或者vite build命令入手,接下來從vite命令入手研究一下。
1.2 vite的命令定義在何處?
看一下vite的package.json文件(路徑:vite/packages/vite/package.json):
當我們使用vite命令的時候,會執行bin目錄下的vite.js文件,看一下這個文件(路徑:vite/packages/vite/bin/vite.js):
可以看到這段代碼的關鍵是執行start方法,而start方法是導入打包后的cli.js文件,那么這個打包后的文件對應的原文件是哪個文件呢?vite打包的時候是使用rollup的,所以我們看一下rollup的配置文件(路徑:vite/packages/vite/rollup.config.ts):
如上代碼可以看出vite相關命令的定義在/src/node/cli.ts 這個文件當中。
1.3 vite的命令是如何定義的?
我們來看一下vite的命令是如何定義的(路徑:vite/packages/vite/src/node/cli.ts):
import { cac } from 'cac'
const cli = cac('vite')
cli
.option('-m, --mode <mode>', `[string] set env mode`)
cli
.command('[root]', 'start dev server') // default command
.alias('serve') // the command is called 'serve' in Vite's API
.option('--port <port>', `[number] specify port`)
.action(async (root: string, options: ServerOptions & GlobalCLIOptions) => {
const { createServer } = await import('./server')
try {
const server = await createServer({
root,
base: options.base,
mode: options.mode,
configFile: options.config,
logLevel: options.logLevel,
clearScreen: options.clearScreen,
optimizeDeps: { force: options.force },
server: cleanOptions(options),
})
})
如上代碼所示,vite主要是使用cac這個命令行工具庫定義命令的,解釋一下這里使用到的cac的相關API:
cac(name?):用于創建一個cac實例,name參數是可選的。
option(name, description, config): 用于設置配置項。
command(name, description, config?): 聲明cli命令,可以給命令設置獨立的配置項喝其配置后的執行動作。
command.alias(name):給cli命令起別名。
action(callback): 指定命令所執行的操作。
可以看出,當運行vite命令的時候會執行createServer方法,我們這里要注意參數mode就是我們運行命令時通過--mode 或者 -m指定的參數,下面來研究createServer方法。
看一下createServer方法(路徑:createServervite/packages/vite/src/node/server/index.ts):
import { resolveConfig } from '../config'
export async function createServer(
inlineConfig: InlineConfig = {},
): Promise<ViteDevServer> {
const config = await resolveConfig(inlineConfig, 'serve')
}
可以看到createServer方法調用的是resolveConfig方法,下面看一下resolveConfig方法。
resolveConfig方法的代碼如下(路徑;vite/packages/vite/src/node/config.ts):
import { loadEnv, resolveEnvPrefix } from './env'
export async function resolveConfig(
inlineConfig: InlineConfig,
command: 'build' | 'serve',
defaultMode = 'development',
defaultNodeEnv = 'development',
): Promise<ResolvedConfig> {
const envDir = config.envDir
? normalizePath(path.resolve(resolvedRoot, config.envDir))
: resolvedRoot
const userEnv =
inlineConfig.envFile !== false &&
loadEnv(mode, envDir, resolveEnvPrefix(config))
const resolvedConfig: ResolvedConfig = {
command,
mode,
env: {
...userEnv,
BASE_URL,
MODE: mode,
DEV: !isProduction,
PROD: isProduction,
},
}
const resolved: ResolvedConfig = {
...config,
...resolvedConfig,
}
return resolved
}
可以看到resolveConfig的主要工作:
首先確定.env文件的路徑
然后調用loadEnv方法加載解析.env文件,將結果賦值給userEnv
最后返回整個解析后的配置
我們看到這里的關鍵代碼是loadEnv(mode, envDir, resolveEnvPrefix(config))
下面我就重點看一下loadEnv方法。
loadEnv方法是vite中一個比較核心的方法,也作為vite對外提供的一個JavaScript API,用于加載 envDir 中的 .env 文件。
我們看一下loadEnv方法(路徑:vite/packages/vite/src/node/env.ts):
import { parse } from 'dotenv'
import { arraify, lookupFile } from './utils'
export function loadEnv(
mode: string,
envDir: string,
prefixes: string | string[] = 'VITE_',
): Record<string, string> {
prefixes = arraify(prefixes)
const env: Record<string, string> = {}
const envFiles = [
/** default file */ `.env`,
/** local file */ `.env.local`,
/** mode file */ `.env.${mode}`,
/** mode local file */ `.env.${mode}.local`,
]
const parsed = Object.fromEntries(
envFiles.flatMap((file) => {
const path = lookupFile(envDir, [file], {
pathOnly: true,
rootDir: envDir,
})
if (!path) return []
return Object.entries(parse(fs.readFileSync(path)))
}),
)
// only keys that start with prefix are exposed to client
for (const [key, value] of Object.entries(parsed)) {
if (prefixes.some((prefix) => key.startsWith(prefix))) {
env[key] = value
} else if (
key === 'NODE_ENV' &&
process.env.VITE_USER_NODE_ENV === undefined
) {
// NODE_ENV override in .env file
process.env.VITE_USER_NODE_ENV = value
}
}
return env
}
如上代碼所示理解loadEnv方法注意以下幾個方面:
該方法接收三個參數,分別是模式、.env文件的路徑還有環境變量的前綴。
使用遞歸方法lookupFile找到.env文件的路徑,使用fs.readFileSync讀取文件。
使用dotenv提供的方法解析.env文件內容。
關于dotenv可以學習川哥的文章,也可以看看筆者的源碼共讀語雀筆記。至此,我們了解了vite是如何讀取.env文件中定義的環境變量了。下面我們研究第二個問題vite如何將.env中配置的環境變量掛載到import.meta.env環境變量上。
2.1 vite的環境變量和import.meta
Vite 在一個特殊的 import.meta.env 對象上暴露環境變量,有一些在所有情況下都可以使用的內建變量:
import.meta.env.MODE: {string} 應用運行的模式。
import.meta.env.BASE_URL: {string} 部署應用時的基本 URL。他由base 配置項決定。
import.meta.env.PROD: {boolean} 應用是否運行在生產環境。
import.meta.env.DEV: {boolean} 應用是否運行在開發環境 (永遠與 import.meta.env.PROD相反)。
import.meta.env.SSR: {boolean} 應用是否運行在 server 上。
詳見環境變量。這里我們要解釋一下import.meta。它是一個給JavaScript模塊暴露特定上下文的元數據屬性的對象。它包含了這個模塊的信息,比如說這個模塊的URL。詳見import.meta 的MDN文檔。需要注意不可以在模塊的外部使用import.meta,如下圖所示:
2.2 resolveConfig
在上文中我們已經研究了resolveConfig的代碼,我們再來看以下此方法中的另一段代碼(路徑:vite/packages/vite/src/node/config.ts):
import {resolvePlugins,} from './plugins'
export async function resolveConfig(
inlineConfig: InlineConfig,
command: 'build' | 'serve',
defaultMode = 'development',
defaultNodeEnv = 'development',
): Promise<ResolvedConfig> {
(resolved.plugins as Plugin[]) = await resolvePlugins(
resolved,
prePlugins,
normalPlugins,
postPlugins,
)
}
這里調用了resolvePlugins,接收resolved對象,此對象中含有開發者所指定的模式以及.env文件中的環境變量。我們接著看一下resolvePlugins方法。
2.3 resolvePlugins
節選resolvePlugins方法如下(路徑:vite/packages/vite/src/node/plugins/index.ts):
import { definePlugin } from './define'
export async function resolvePlugins(
config: ResolvedConfig,
prePlugins: Plugin[],
normalPlugins: Plugin[],
postPlugins: Plugin[],
): Promise<Plugin[]> {
return [
//...
definePlugin(config),
//...
].filter(Boolean) as Plugin[]
}
resolvePlugins負責解析插件,這里面調用了definePlugin方法,我們看一下。
2.4 definePlugin
definePlugin的代碼如下(路徑:vite/packages/vite/src/node/plugins/define.ts):
const importMetaKeys: Record<string, string> = {}
const importMetaFallbackKeys: Record<string, string> = {}
if (isBuild) {
const env: Record<string, any> = {
...config.env,
SSR: !!config.build.ssr,
}
for (const key in env) {
importMetaKeys[`import.meta.env.${key}`] = JSON.stringify(env[key])
}
Object.assign(importMetaFallbackKeys, {
'import.meta.env.': `({}).`,
'import.meta.env': JSON.stringify(config.env),
'import.meta.hot': `false`,
})
}
這段代碼的關鍵部分在于第8-10行的for循環,將.env文件中定義的環境變量掛在到了import.meta.env上。至此,如何也了解了vite是如何將環境變量掛在到import.meta.env環境變量上。
關于“vite是如何解析.env文件的”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。