配置读取
1. 概述
梵医云前端项目使用多环境配置管理,支持开发、测试、预发布、生产等多种环境。配置系统基于 Vite 的环境变量机制,结合 TypeScript 类型安全,提供灵活的配置读取方式。
1.1 配置特点
- 多环境支持:支持开发、测试、预发布、生产等多种环境
- 类型安全:完整的 TypeScript 类型定义
- 集中管理:所有配置集中在配置文件中
- 动态加载:根据运行环境自动加载对应配置
- 易于扩展:支持自定义配置项
1.2 配置文件结构
fanyi-cloud-ui/
├── .env # 基础配置
├── .env.local # 本地环境配置
├── .env.dev # 开发环境配置
├── .env.test # 测试环境配置
├── .env.stage # 预发布环境配置
├── .env.prod # 生产环境配置
├── vite.config.ts # Vite配置
├── tsconfig.json # TypeScript配置
├── package.json # 项目配置
└── src/
└── config/
└── axios/
├── config.ts # Axios配置
├── index.ts # 请求方法封装
└── errorCode.ts # 错误码配置2. 环境变量配置
2.1 环境变量说明
项目使用 Vite 的环境变量机制,所有以 VITE_ 开头的变量都会暴露给客户端代码。
基础配置(.env)
bash
# 标题
VITE_APP_TITLE=梵医云管理系统
# 项目本地运行端口号
VITE_PORT=80
# open 运行 npm run dev 时自动打开浏览器
VITE_OPEN=true
# 租户开关
VITE_APP_TENANT_ENABLE=true
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true
# 文档地址的开关
VITE_APP_DOCALERT_ENABLE=true
# 百度统计
VITE_APP_BAIDU_CODE =
# 默认账户密码
VITE_APP_DEFAULT_LOGIN_TENANT =梵医云
VITE_APP_DEFAULT_LOGIN_USERNAME = ''
VITE_APP_DEFAULT_LOGIN_PASSWORD = ''开发环境配置(.env.dev)
bash
NODE_ENV=production
VITE_DEV=true
# 请求路径
VITE_BASE_URL = 'http://192.168.31.10:48080'
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
VITE_UPLOAD_TYPE=server
# 上传路径
VITE_UPLOAD_URL='http://192.168.31.10:48080/admin-api/infra/file/upload'
# 上传.apk
VITE_UPLOAD_FILE_URL='http://192.168.31.10:48080/admin-api/infra/file/uploadNew'
# 接口地址
VITE_API_URL=/admin-api
# 是否删除debugger
VITE_DROP_DEBUGGER=false
# 是否删除console.log
VITE_DROP_CONSOLE=false
# 是否sourcemap
VITE_SOURCEMAP=true
# 打包路径
VITE_BASE_PATH=/
# 输出路径
VITE_OUT_DIR=dist
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='https://web.fanyicloud.com.cn'
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true
# 聊天上传文件的路径
VITE_CHAT_UPLOAD_URL='http://192.168.31.10:48080/admin-api/chat/file/upload'2.2 环境变量读取
在代码中读取环境变量:
typescript
// 读取环境变量
const baseUrl = import.meta.env.VITE_BASE_URL
const apiUrl = import.meta.env.VITE_API_URL
const port = import.meta.env.VITE_PORT
const title = import.meta.env.VITE_APP_TITLE
// 组合使用
const fullUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL2.3 环境变量类型定义
为了获得类型安全,可以定义环境变量的类型:
typescript
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_PORT: number
readonly VITE_OPEN: string
readonly VITE_APP_TENANT_ENABLE: string
readonly VITE_APP_CAPTCHA_ENABLE: string
readonly VITE_APP_DOCALERT_ENABLE: string
readonly VITE_APP_BAIDU_CODE: string
readonly VITE_APP_DEFAULT_LOGIN_TENANT: string
readonly VITE_APP_DEFAULT_LOGIN_USERNAME: string
readonly VITE_APP_DEFAULT_LOGIN_PASSWORD: string
readonly VITE_DEV: string
readonly VITE_BASE_URL: string
readonly VITE_UPLOAD_TYPE: string
readonly VITE_UPLOAD_URL: string
readonly VITE_UPLOAD_FILE_URL: string
readonly VITE_API_URL: string
readonly VITE_DROP_DEBUGGER: string
readonly VITE_DROP_CONSOLE: string
readonly VITE_SOURCEMAP: string
readonly VITE_BASE_PATH: string
readonly VITE_OUT_DIR: string
readonly VITE_MALL_H5_DOMAIN: string
readonly VITE_CHAT_UPLOAD_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}3. Vite配置
3.1 配置文件位置
[vite.config.ts](file:///e:\git.fanyicloud.com.cn\fanyi-cloud-ui\vite.config.ts)
3.2 基础配置
typescript
import { resolve } from 'path'
import { loadEnv } from 'vite'
import type { UserConfig, ConfigEnv } from 'vite'
import { createVitePlugins } from './build/vite'
// 当前执行node命令时文件夹的地址(工作目录)
const root = process.cwd()
// 路径查找
function pathResolve(dir: string) {
return resolve(root, '.', dir)
}
export default ({ command, mode }: ConfigEnv): UserConfig => {
let env = {} as any
const isBuild = command === 'build'
if (!isBuild) {
env = loadEnv((process.argv[3] === '--mode' ? process.argv[4] : process.argv[3]), root)
} else {
env = loadEnv(mode, root)
}
return {
// 配置项...
}
}3.3 服务器配置
typescript
server: {
port: env.VITE_PORT, // 端口号
host: "0.0.0.0",
open: env.VITE_OPEN === 'true',
// 本地跨域代理
proxy: {
['/admin-api']: {
target: env.VITE_BASE_URL,
ws: false,
changeOrigin: true,
rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''),
},
},
}3.4 路径别名配置
typescript
resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.scss', '.css'],
alias: [
{
find: 'vue-i18n',
replacement: 'vue-i18n/dist/vue-i18n.cjs.js'
},
{
find: /\@\//,
replacement: `${pathResolve('src')}/`
}
]
}3.5 CSS配置
typescript
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler',
silenceDeprecations: ['legacy-js-api'],
additionalData: `@use "./src/styles/variables.scss" as *;`,
javascriptEnabled: true
}
}
}3.6 构建配置
typescript
build: {
minify: 'terser',
outDir: env.VITE_OUT_DIR || 'dist',
sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
terserOptions: {
compress: {
drop_debugger: env.VITE_DROP_DEBUGGER === 'true',
drop_console: env.VITE_DROP_CONSOLE === 'true'
}
},
rollupOptions: {
output: {
manualChunks: {
echarts: ['echarts'] // 将 echarts 单独打包
}
},
},
}3.7 依赖优化配置
typescript
optimizeDeps: { include, exclude }4. TypeScript配置
4.1 配置文件位置
[tsconfig.json](file:///e:\git.fanyicloud.com.cn\fanyi-cloud-ui\tsconfig.json)
4.2 基础配置
json
{
"compilerOptions": {
"module": "es2020",
"target": "esnext",
"useDefineForClassFields": true,
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"baseUrl": "./",
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"strictFunctionTypes": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"experimentalDecorators": true,
"noImplicitAny": false,
"skipLibCheck": true,
"paths": {
"@/*": ["src/*"]
},
"types": [
"vite/client",
"@intlify/unplugin-vue-i18n/types",
"element-plus/global",
"@types/qrcode",
"vite-plugin-svg-icons/client"
],
"outDir": "target",
"typeRoots": ["./node_modules/@types/", "./types"]
},
"include": [
"src",
"types/**/*.d.ts",
"src/types/auto-imports.d.ts",
"src/types/auto-components.d.ts"
],
"exclude": ["dist", "target", "node_modules"]
}4.3 路径别名
typescript
// 使用 @ 别名引用
import { getDictOptions } from '@/utils/dict'
import { useUserStore } from '@/store/modules/user'5. Axios配置
5.1 配置文件位置
- [config.ts](file:///e:\git.fanyicloud.com.cn\fanyi-cloud-ui\src\config\axios\config.ts) - Axios基础配置
- [index.ts](file:///e:\git.fanyicloud.com.cn\fanyi-cloud-ui\src\config\axios\index.ts) - 请求方法封装
- [errorCode.ts](file:///e:\git.fanyicloud.com.cn\fanyi-cloud-ui\src\config\axios\errorCode.ts) - 错误码配置
5.2 基础配置
typescript
const config: {
base_url: string
result_code: number | string
default_headers: AxiosHeaders
request_timeout: number
service_instance_tag: string
} = {
/**
* api请求基础路径
*/
base_url: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL,
/**
* 接口成功返回状态码
*/
result_code: 200,
/**
* 接口请求超时时间
*/
request_timeout: 60000,
/**
* 默认接口请求类型
* 可选值:application/x-www-form-urlencoded multipart/form-data
*/
default_headers: 'application/json',
/**
* 网关选择微服务时使用指定的TAG的实例。空值则为默认。
*/
service_instance_tag: import.meta.env.VITE_SERVICE_INSTANCE_TAG
}5.3 请求方法封装
typescript
import { service } from './service'
import { config } from './config'
const { default_headers } = config
const request = (option: any) => {
const { url, method, params, data, headersType, responseType, ...config } = option
return service({
url: url,
method,
params,
data,
...config,
responseType: responseType,
headers: {
'Content-Type': headersType || default_headers
}
})
}
export default {
get: async <T = any>(option: any) => {
const res = await request({ method: 'GET', ...option })
return res.data as unknown as T
},
post: async <T = any>(option: any) => {
const res = await request({ method: 'POST', ...option })
return res.data as unknown as T
},
postOriginal: async (option: any) => {
const res = await request({ method: 'POST', ...option })
return res
},
delete: async <T = any>(option: any) => {
const res = await request({ method: 'DELETE', ...option })
return res.data as unknown as T
},
put: async <T = any>(option: any) => {
const res = await request({ method: 'PUT', ...option })
return res.data as unknown as T
},
download: async <T = any>(option: any) => {
const res = await request({ method: 'GET', responseType: 'blob', ...option })
return res as unknown as Promise<T>
},
upload: async <T = any>(option: any) => {
option.headersType = 'multipart/form-data'
const res = await request({ method: 'POST', ...option })
return res as unknown as Promise<T>
}
}5.4 错误码配置
typescript
export default {
'401': '认证失败,无法访问系统资源',
'403': '当前操作没有权限',
'404': '访问资源不存在',
default: '系统未知错误,请反馈给管理员'
}5.5 使用示例
typescript
import request from '@/config/axios'
// GET请求
const getData = async () => {
const result = await request.get({
url: '/system/user/list',
params: { pageNo: 1, pageSize: 10 }
})
return result
}
// POST请求
const createData = async (data: any) => {
const result = await request.post({
url: '/system/user/create',
data
})
return result
}
// PUT请求
const updateData = async (id: number, data: any) => {
const result = await request.put({
url: `/system/user/update/${id}`,
data
})
return result
}
// DELETE请求
const deleteData = async (id: number) => {
const result = await request.delete({
url: `/system/user/delete/${id}`
})
return result
}
// 下载文件
const downloadFile = async (id: number) => {
const result = await request.download({
url: `/system/user/export/${id}`
})
return result
}
// 上传文件
const uploadFile = async (file: File) => {
const formData = new FormData()
formData.append('file', file)
const result = await request.upload({
url: '/infra/file/upload',
data: formData
})
return result
}6. 项目配置
6.1 配置文件位置
[package.json](file:///e:\git.fanyicloud.com.cn\fanyi-cloud-ui\package.json)
6.2 脚本命令
json
{
"scripts": {
"i": "pnpm install",
"dev": "vite --mode env.local",
"dev-server": "vite --mode dev",
"ts:check": "vue-tsc --noEmit",
"build:local": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build",
"build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev",
"build:test": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode test",
"build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage",
"build:prod": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode prod",
"serve:dev": "vite preview --mode dev",
"serve:prod": "vite preview --mode prod",
"preview": "pnpm build:local && vite preview",
"clean": "npx rimraf node_modules",
"clean:cache": "npx rimraf node_modules/.cache",
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
"lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
"lint:style": "stylelint --fix \"./src/**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ",
"prepare": "husky"
}
}6.3 运行环境说明
| 命令 | 环境 | 说明 |
|---|---|---|
pnpm dev | 本地环境 | 本地开发,使用 .env.local 配置 |
pnpm dev-server | 开发环境 | 连接开发服务器,使用 .env.dev 配置 |
pnpm build:local | 本地构建 | 本地打包,使用 .env 配置 |
pnpm build:dev | 开发构建 | 开发环境打包,使用 .env.dev 配置 |
pnpm build:test | 测试构建 | 测试环境打包,使用 .env.test 配置 |
pnpm build:stage | 预发布构建 | 预发布环境打包,使用 .env.stage 配置 |
pnpm build:prod | 生产构建 | 生产环境打包,使用 .env.prod 配置 |
6.4 依赖管理
json
{
"engines": {
"node": ">= 16.0.0",
"pnpm": ">=8.6.0"
}
}7. 配置读取最佳实践
7.1 环境变量命名规范
- 使用
VITE_前缀暴露给客户端 - 使用大写字母和下划线
- 使用描述性的名称
bash
# 好的做法
VITE_BASE_URL=http://api.example.com
VITE_API_TIMEOUT=60000
VITE_ENABLE_FEATURE_X=true
# 不好的做法
base_url=http://api.example.com
apiTimeout=60000
enableFeatureX=true7.2 配置文件组织
- 基础配置放在
.env文件中 - 环境特定配置放在对应的
.env.{environment}文件中 - 敏感信息不要提交到版本控制
7.3 类型安全
为配置项定义类型,获得更好的开发体验:
typescript
interface AppConfig {
title: string
port: number
baseUrl: string
apiUrl: string
enableTenant: boolean
enableCaptcha: boolean
}
const config: AppConfig = {
title: import.meta.env.VITE_APP_TITLE,
port: Number(import.meta.env.VITE_PORT),
baseUrl: import.meta.env.VITE_BASE_URL,
apiUrl: import.meta.env.VITE_API_URL,
enableTenant: import.meta.env.VITE_APP_TENANT_ENABLE === 'true',
enableCaptcha: import.meta.env.VITE_APP_CAPTCHA_ENABLE === 'true'
}7.4 配置验证
对配置项进行验证,确保配置正确:
typescript
const validateConfig = () => {
const required = [
'VITE_BASE_URL',
'VITE_API_URL',
'VITE_APP_TITLE'
]
const missing = required.filter(key => !import.meta.env[key])
if (missing.length > 0) {
throw new Error(`缺少必需的环境变量: ${missing.join(', ')}`)
}
}
validateConfig()7.5 默认值处理
为配置项提供默认值:
typescript
const getEnv = (key: string, defaultValue: string = '') => {
return import.meta.env[key] || defaultValue
}
const config = {
baseUrl: getEnv('VITE_BASE_URL', 'http://localhost:8080'),
apiUrl: getEnv('VITE_API_URL', '/api'),
timeout: Number(getEnv('VITE_API_TIMEOUT', '60000'))
}8. 常见问题
8.1 环境变量不生效
问题原因:
- 环境变量没有以
VITE_开头 - 修改了环境变量但没有重启开发服务器
- 使用了错误的环境文件
解决方案:
bash
# 确保环境变量以 VITE_ 开头
VITE_BASE_URL=http://api.example.com
# 重启开发服务器
pnpm dev
# 使用正确的环境文件
pnpm dev-server # 使用 .env.dev8.2 路径别名不生效
问题原因:
- TypeScript 配置没有正确设置
- Vite 配置没有正确设置
解决方案:
typescript
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}
// vite.config.ts
{
resolve: {
alias: [
{
find: /\@\//,
replacement: `${pathResolve('src')}/`
}
]
}
}8.3 请求超时
问题原因:
- 请求超时时间设置过短
- 网络问题
解决方案:
typescript
// 修改配置
const config = {
request_timeout: 60000 // 60秒
}
// 或者在请求时指定
await request.get({
url: '/api/data',
timeout: 120000 // 120秒
})8.4 跨域问题
问题原因:
- 后端没有配置 CORS
- 代理配置不正确
解决方案:
typescript
// vite.config.ts
server: {
proxy: {
['/admin-api']: {
target: env.VITE_BASE_URL,
ws: false,
changeOrigin: true,
rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''),
},
},
}注意:本文档持续更新中,如有问题请及时反馈。
