Skip to content

配置读取

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_URL

2.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=true

7.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.dev

8.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`), ''),
    },
  },
}

注意:本文档持续更新中,如有问题请及时反馈。