1. 为什么Vue项目需要环境变量
在Vue项目开发中,环境变量就像是一个智能开关面板,它能让我们在不同环境下自动切换配置。想象一下你正在开发一个电商平台:开发时用测试API地址,上线后要切换生产环境API;开发阶段需要开启调试工具,生产环境必须关闭;不同部署服务器可能有不同的资源路径...手动修改这些配置不仅容易出错,在团队协作中更是灾难。
环境变量的核心价值在于:
- 环境隔离:一套代码适配开发、测试、生产多环境
- 敏感信息保护:API密钥等敏感数据不硬编码在源码中
- 构建优化:根据环境决定是否启用代码压缩、sourcemap等
- 部署便利:同一构建产物通过环境变量适配不同服务器
我接手过一个Vue2老项目,所有配置都直接写在src/config.js里。每次发版前要手动改3处API地址,有次测试环境漏改了一处,导致线上订单全部发往测试服务器。迁移到环境变量后,这类问题彻底消失。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Vue环境变量工作机制
2.1 底层原理:webpack的DefinePlugin
Vue CLI底层通过webpack的DefinePlugin实现环境变量注入。这个插件会在编译阶段执行文本替换,相当于全局查找替换。例如配置VUE_APP_API_URL: 'https://dev.example.com'后:
javascript复制// 源码中写入
console.log(process.env.VUE_APP_API_URL)
// 编译后实际变成
console.log('https://dev.example.com')
重要提示:环境变量注入发生在构建时而非运行时,修改环境变量必须重新构建项目
2.2 变量命名规则
Vue CLI强制要求自定义环境变量必须以VUE_APP_开头,这是为了防止意外暴露系统级变量。以下是一些典型示例:
bash复制# 有效的
VUE_APP_TITLE=电商后台
VUE_APP_SENTRY_DSN=https://xxx.ingest.sentry.io/xxx
# 无效的(不会被打包进客户端代码)
DB_PASSWORD=123456
NODE_ENV=production
2.3 多环境文件支持
Vue CLI按照以下优先级加载.env文件:
.env.[mode].local.env.[mode].env.local.env
比如执行npm run serve时(mode=development):
- 先加载
.env.development.local - 再加载
.env.development - 最后加载
.env
3. 实战配置指南
3.1 基础环境设置
在项目根目录创建以下文件:
code复制.env # 所有环境共享
.env.development # 开发环境
.env.production # 生产环境
.env.staging # 预发布环境
.env.development示例:
bash复制VUE_APP_ENV=development
VUE_APP_API_BASE=https://dev-api.example.com
VUE_APP_DEBUG=true
.env.production示例:
bash复制VUE_APP_ENV=production
VUE_APP_API_BASE=https://api.example.com
VUE_APP_SENTRY_DSN=https://xxx@sentry.io/xxx
3.2 在代码中使用
javascript复制// axios实例配置
const service = axios.create({
baseURL: process.env.VUE_APP_API_BASE,
timeout: 15000
})
// 条件编译
if (process.env.VUE_APP_DEBUG) {
console.log('[当前环境]', process.env.VUE_APP_ENV)
}
// 模板中使用(需在vue.config.js中配置)
<template>
<div>{{ $env.VUE_APP_VERSION }}</div>
</template>
3.3 高级配置技巧
在vue.config.js中暴露变量到模板:
javascript复制module.exports = {
chainWebpack: config => {
config.plugin('html').tap(args => {
args[0].env = process.env
return args
})
}
}
动态加载不同环境CSS:
css复制/* src/styles/env.scss */
@mixin dev-theme {
body {
background-color: #f0f0f0;
&::after {
content: '开发环境';
position: fixed;
bottom: 0;
right: 0;
}
}
}
@mixin prod-theme {
body {
background-color: #fff;
}
}
@if process.env.VUE_APP_ENV == 'development' {
@include dev-theme;
} @else {
@include prod-theme;
}
4. 常见问题解决方案
4.1 环境变量未生效
排查步骤:
- 确认变量名以
VUE_APP_开头 - 检查.env文件是否放在项目根目录(与package.json同级)
- 确认文件名与mode匹配(
npm run serve对应development) - 重启开发服务器(修改.env需要重新启动)
4.2 敏感信息泄露防护
错误做法:
javascript复制// 直接在前端代码中使用数据库密码
const DB_PASS = process.env.DB_PASSWORD
正确方案:
- 后端敏感配置通过服务端环境变量设置
- 前端只能暴露
VUE_APP_开头的变量 - 敏感操作通过API接口中转
4.3 多团队协作方案
建议目录结构:
code复制/config
/env
.env.dev-team1 # 第一组开发配置
.env.dev-team2 # 第二组开发配置
.env.staging # 测试环境
.env.prod # 生产环境
env-loader.js # 环境加载逻辑
env-loader.js示例:
javascript复制const fs = require('fs')
const path = require('path')
module.exports = (mode) => {
const basePath = path.resolve(__dirname, './env')
const files = [
`.env.${mode}`,
`.env.${mode}.local`,
'.env'
]
files.forEach(file => {
const filePath = path.join(basePath, file)
if (fs.existsSync(filePath)) {
require('dotenv').config({ path: filePath })
}
})
}
5. 企业级实践建议
5.1 环境变量管理规范
-
命名规范:
- 组前缀:
VUE_APP_[组名]_[功能] - 示例:
VUE_APP_PAY_WECHAT_APPID
- 组前缀:
-
版本控制:
- 将
.env.example纳入版本库 - 真实
.env文件加入.gitignore - 敏感变量通过CI/CD工具注入
- 将
-
类型校验:
javascript复制// src/utils/envChecker.js
const requiredVars = [
'VUE_APP_API_BASE',
'VUE_APP_SENTRY_DSN'
]
export function checkEnv() {
requiredVars.forEach(varName => {
if (!process.env[varName]) {
console.error(`Missing required env variable: ${varName}`)
if (process.env.NODE_ENV === 'development') {
throw new Error(`环境变量检查失败`)
}
}
})
}
5.2 CI/CD集成示例
GitLab CI配置示例:
yaml复制stages:
- build
build_production:
stage: build
only:
- master
script:
- echo "VUE_APP_COMMIT_SHA=$CI_COMMIT_SHA" >> .env.production
- echo "VUE_APP_BUILD_DATE=$(date +'%Y-%m-%d %H:%M:%S')" >> .env.production
- npm install
- npm run build
artifacts:
paths:
- dist/
5.3 监控与调试
生产环境变量检查:
javascript复制// main.js
if (process.env.VUE_APP_ENV === 'production') {
Sentry.init({
dsn: process.env.VUE_APP_SENTRY_DSN,
release: process.env.VUE_APP_VERSION
})
// 打印关键环境变量哈希用于问题排查
console.log(
'EnvHash:',
md5(JSON.stringify({
API: process.env.VUE_APP_API_BASE,
ENV: process.env.VUE_APP_ENV,
VERSION: process.env.VUE_APP_VERSION
}))
)
}
动态调试技巧:
javascript复制// 在浏览器控制台临时查看(需配置vue.config.js)
window.__env = process.env
// 或者通过URL参数覆盖
// http://localhost:8080/?env=DEBUG:true
const urlParams = new URLSearchParams(window.location.search)
if (urlParams.has('env')) {
urlParams.get('env').split(',').forEach(pair => {
const [key, value] = pair.split(':')
process.env[key] = value
})
}
6. Vue3特别注意事项
6.1 Vite环境变量差异
Vue3+Vite项目需要注意:
- 变量前缀变为
VITE_而非VUE_APP_ - 访问方式改为
import.meta.env.VITE_XXX .env文件加载逻辑与Vue CLI不同
6.2 组合式API使用模式
typescript复制// src/composables/useEnv.ts
import { computed } from 'vue'
export default function useEnv() {
const isDev = computed(() => import.meta.env.MODE === 'development')
const apiBase = computed(() => import.meta.env.VITE_API_BASE)
return {
isDev,
apiBase
}
}
6.3 TypeScript支持
env.d.ts类型声明:
typescript复制/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_API_BASE: string
// 更多环境变量...
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
