业务背景
随着前端技术的快速发展,现在的前端项目不仅需要满足业务需求,还需要满足许多复杂的环境适配场景,例如:
- 每个环境的接口请求地址是不同的;
- 每个环境可能有不同的业务逻辑;
- ...
这些都可以归纳为 环境变量 的注入或多环境适配问题,今天我们就来聊下如何优雅的 Vite
中解决此问题。
目标
- 支持多环境适配
- 支持构建产物与环境无关
如上图所示,在项目启动 或构建时,通过注入环境变量实现不同环境的差异性配置,构建产物也可通过脚本替换原有环境变量,以达到构建产物与环境无关的目标
多环境适配
环境变量加载
Vite 环境变量和模式
- 通过
*.loacl
解决本地开发和线上环境变量不一致问题 - 多环境可通过
mode
解决多环境适配问题
sh
.env # 所有情况下都会加载
.env.local # 所有情况下都会加载,但会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略
加载优先级
- 指定模式的文件(例如 .env.prod)会比通用形式的优先级更高(例如 .env)
- Vite 执行时已经存在的环境变量有最高的优先级,不会被 .env 类文件覆盖
多模式文件配置
Vite
通过多模式来配置不同启动场景下的特性环境变量,你可以创建自定义的模式文件,如下:
上面的示例项目创建了两种模式分别兼容 dev
、test
环境,每种模式下都可以对设置不同的环境变量
json
{
"name": "vite-meta-env",
"scripts": {
"start": "vite",
"start:dev": "vite --mode dev",
"start:test": "vite --mode test",
"build": "tsc && vite build",
"build:dev": "vite build --mode dev",
"build:test": "vite build --mode test",
}
}
根据 Vite 的约定规则,只有以VITE_
开头的变量才会在客户端被获取,获取方式为:import.meta.env.{参数名}
。如想添加自己的独特前缀的可通过设置 envPrefix
实现,示例代码如下:
ts
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
const envPrefix = ['VITE_', 'S_'];
export default defineConfig(({ mode }) => {
// 获取环境变量
const env = loadEnv(mode, __dirname, envPrefix);
return {
envPrefix,
plugins: [
react()
],
}
})
构建产物与环境无关
借助 import-meta-env 提供的能力使构建产物与环境无关
安装依赖
sh
pnpm i -D @import-meta-env/cli
添加替换入口
html
<!-- index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>多环境适配示例</title>
<!-- 新增 -->
<script>
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
添加 .env.example
明确需要加载的环境变量清单
sh
# .env.example
S_API_HOST=https://xx.com
添加替换脚本
json
{
"scripts": {
"deploy:env": "S_API_HOST=https://test.xx.com import-meta-env -x .env.example -p dist/index.html"
}
}
添加获取环境变量工具方法
ts
// src/utils/env.ts
/**
* 环境变量表达式是静态转换的,必须使用完整的静态字符串来引用它们。
* 以下方式获取不到
* import.meta.env
* import.meta.env['FOO']
*/
const envMap: Record<string, string> = {
S_API_HOST: import.meta.env.S_API_HOST,
};
export const getEnvValue = (key: string): string => {
if (import.meta.env.DEV) {
return envMap[key];
}
try {
return window.import_meta_env[key] || envMap[key];
} catch (error) {
return '';
}
};
结语
本篇文章到这里就结束了,如果文章对你有用,请收藏起来,方便后续查看,如果你有更好的见解,欢迎指正~
本文的代码已整理到 vite-meta-env 仓库中,有需要的童鞋请自取