配置

模板通过两类环境变量管理配置:构建期固化的 VITE_*,以及生产环境可部署后修改的 APP_PUBLIC_*

能力职责
Vite 应用配置工厂@uniboot/vite-configdefineApplicationConfig:统一 Vue / 插件 / base / chunks;默认内置运行时配置插件
运行时配置(构建)@uniboot/app-config/viteviteAppConfigPlugin:生产构建写出 _app-config-*.js
运行时配置(读取)@uniboot/app-configgetPublicRuntimeConfig / getPublicEnv:按完整键名读取

模板推荐:vite.config.ts@uniboot/vite-config,业务侧从 @/config 读配置(内部用 @uniboot/app-config)。

环境变量配置

项目的环境变量配置位于应用目录下的 .env.env.development.env.production

规则与 Vite Env Variables and Modes 一致。格式如下:

bash
.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]         # 只在指定的模式中被载入
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略

TIP

两类变量用途不同:

前缀写入位置修改方式
VITE_*打进 JS bundle(如页面标题).env 后重新构建
APP_PUBLIC_*生产环境的 _app-config-{version}-{hash}.js部署后可直接改 dist 内配置文件
  • Vite 默认只把 VITE_ 暴露给 import.meta.envdefineApplicationConfig 已设置 envPrefix: ['VITE_', 'APP_PUBLIC_'],因此开发时也可以:

    ts
    console.log(import.meta.env.VITE_PORT)
    console.log(import.meta.env.APP_PUBLIC_API_URL)
  • 生产构建时,APP_PUBLIC_*保留完整键名写入 _app-config-{version}-{hash}.js,并挂到 window.__UNIBOOT_ADMIN_APP_CONFIG__

  • 不要.env* 里写 NODE_ENV。Vite 8 会警告并忽略(production 尤其无效);模式由 vite / vite build 决定。

环境配置说明

bash
# 项目名称(展示用,构建期固化)
VITE_APP_TITLE=UNIBOOT ADMIN
# 项目版本(同时用于配置文件名中的 version)
VITE_APP_VERSION=1.0.0
bash
# 启动端口(优先于 defineApplicationConfig 的 port 回退值)
VITE_PORT=3000

APP_PUBLIC_APP_BASE=/merchant-web
APP_PUBLIC_CLIENT_ID=koms
APP_PUBLIC_REDIRECT_URI=http://192.168.10.68:3000/merchant-web
APP_PUBLIC_API_URL=https://merchant-dev.klond.com.cn/merchant-api
APP_PUBLIC_AUTH_SERVICE_URL=https://merchant-dev.klond.com.cn/auth-service
APP_PUBLIC_ADMIN_SERVICE_URL=https://merchant-dev.klond.com.cn/admin-service
bash
APP_PUBLIC_APP_BASE=/merchant-web
APP_PUBLIC_CLIENT_ID=koms
APP_PUBLIC_REDIRECT_URI=https://merchant-sit.klond.com.cn/merchant-web
APP_PUBLIC_API_URL=https://merchant-sit.klond.com.cn/merchant-api
APP_PUBLIC_AUTH_SERVICE_URL=https://merchant-sit.klond.com.cn/auth-service
APP_PUBLIC_ADMIN_SERVICE_URL=https://merchant-sit.klond.com.cn/admin-service

.env 键名与 src/config 导出字段一致(不做 camelCase 映射):

configs / 配置文件键说明
APP_PUBLIC_APP_BASE应用部署路径前缀,须与 Vite base、Nginx location 一致
APP_PUBLIC_CLIENT_IDOAuth 客户端 ID
APP_PUBLIC_API_URL业务 API 地址
APP_PUBLIC_AUTH_SERVICE_URL授权中心
APP_PUBLIC_ADMIN_SERVICE_URL用户中心
APP_PUBLIC_REDIRECT_URI登录重定向地址
VITE_APP_TITLE / VITE_APP_VERSION构建期标题与版本(打进 bundle,不进 _app-config

另有代码常量(如 timeoutauthMethodurlPrefixproxyhttpHeaders)写在 src/config/index.ts,不走环境变量。


Vite 配置工厂(@uniboot/vite-config

模板的 vite.config.ts 使用 defineApplicationConfig,把常见 Vite 基线收进工厂,业务只保留差异项(路径、dedupe、端口回退等)。

安装

模板一般已内置。新项目:

bash
pnpm add @uniboot/app-config uniboot-ui
pnpm add -D @uniboot/vite-config \
  vite \
  @vitejs/plugin-vue \
  @vitejs/plugin-vue-jsx \
  unplugin-auto-import \
  unplugin-vue-components \
  vite-plugin-svg-icons
  • @uniboot/vite-config:配置工厂(devDependency)
  • @uniboot/app-config:运行时读取(dependency);工厂默认挂载其 ./vite 插件
  • uniboot-ui:工厂内使用 UnibootCombinedResolver,须由业务项目安装

模板用法

与 auth 模板一致:

ts
// vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineApplicationConfig } from '@uniboot/vite-config'

export default defineApplicationConfig({
  // 未设置 VITE_PORT 时的回退端口;有 VITE_PORT 时以 env 为准
  // port: 3000,
  baseFromEnv: 'APP_PUBLIC_APP_BASE',
  srcAlias: fileURLToPath(new URL('./src', import.meta.url)),
  svgIconDirs: [fileURLToPath(new URL('./src/assets/icons', import.meta.url))],
  // 本地 link 模块时建议补全,避免 peer 解析到错误副本
  dedupe: [
    'vue',
    'vue-router',
    'vue-i18n',
    'uniboot-ui',
    '@uniboot/icons-vue',
    '@uniboot/utils',
    '@uniboot/request',
  ],
})

默认内置行为

行为
插件Vue 3、Vue JSX、AutoImport(vue / vue-router + UnibootCombinedResolver)、Components(同上)、可选 SVG icons、viteAppConfigPlugin
envPrefix['VITE_', 'APP_PUBLIC_']
basebaseFromEnv 从 env 读取并归一化尾斜杠;否则用 base(默认 /
端口优先 VITE_PORT;未设置时用选项 port(默认 3000
resolve.alias@srcAlias(默认 cwd/src
resolve.dedupe默认 ['vue'],可覆盖
buildcssMinify: 'esbuild'、按包名 manualChunks、过滤 INVALID_ANNOTATION
defineVue 生产常用开关(Options API / hydration 等)

选项说明

选项说明
port未设置 VITE_PORT 时的回退端口,默认 3000;有 VITE_PORT 时以 env 为准。强制固定端口请用 overrides.server.port
baseVite base;与 baseFromEnv 同时存在时以后者为准
baseFromEnv从该 env key 读 base(如 APP_PUBLIC_APP_BASE),并做尾斜杠归一化
srcAlias@ 别名绝对路径;默认 process.cwd()/src
svgIconDirs有值才启用 vite-plugin-svg-icons
svgSymbolId默认 local-icon-[dir]-[name]
deduperesolve.dedupe;默认 ['vue']
autoImport / components与内置选项浅合并
appConfig传给 viteAppConfigPlugin;传 false 关闭运行时配置插件
plugins追加到内置插件之后
overrides与默认配置做 mergeConfig

覆盖与扩展示例

ts
export default defineApplicationConfig({
  baseFromEnv: 'APP_PUBLIC_APP_BASE',
  srcAlias: fileURLToPath(new URL('./src', import.meta.url)),
  svgIconDirs: [fileURLToPath(new URL('./src/assets/icons', import.meta.url))],
  // 关闭运行时配置文件生成(仅排查用)
  // appConfig: false,
  // 自定义插件选项
  appConfig: {
    windowKey: '__UNIBOOT_ADMIN_APP_CONFIG__',
    envPrefix: 'APP_PUBLIC_',
  },
  plugins: [
    // 追加业务插件
  ],
  overrides: {
    server: {
      // 强制端口,忽略 VITE_PORT
      // port: 5173,
    },
    build: {
      // 局部覆盖 build
    },
  },
})

TIP

本地用 file: 联调 @uniboot/vite-config 时,改完源码并 pnpm build 后,业务仓通常需要再执行一次安装(或 pnpm add -D @uniboot/vite-config@file:...),才能拿到最新 dist


为什么需要运行时配置(viteAppConfigPlugin)

同一份前端产物常会部署到开发、测试、生产等多个环境,接口地址、回调地址往往不同。若这些值写进业务 JS,每次换环境都要重新打包。

viteAppConfigPlugin(由 @uniboot/vite-config 默认挂载,也可从 @uniboot/app-config/vite 单独引入)的做法是:

  1. 开发时:不生成额外文件,继续用 .env.development + import.meta.env,本地改完刷新即可。
  2. 生产构建时:把所有 APP_PUBLIC_* 抽成独立的 _app-config-{version}-{hash}.js,并自动插入 index.html<head> 最前面。
  3. 部署后:运维只需改 dist 里这份配置文件,刷新页面即可生效,无需重新 pnpm build

单独挂插件(不用工厂时)

ts
import { defineConfig } from 'vite'
import { viteAppConfigPlugin } from '@uniboot/app-config/vite'

export default defineConfig(({ command }) => ({
  envPrefix: ['VITE_', 'APP_PUBLIC_'],
  plugins: [viteAppConfigPlugin({ isBuild: command === 'build' })],
}))

TIP

  • 插件内部使用 apply: 'build'只会在生产构建时真正生效
  • 传入 { isBuild: command === 'build' } 与工厂行为一致;若省略参数写成 viteAppConfigPlugin(),效果也相同。
  • { isBuild: false } 或工厂侧 appConfig: false 可强制关闭。

工作原理

text
.env / .env.production


defineApplicationConfig(vite.config.ts)

        ├─ envPrefix / base / plugins / chunks ...
        └─ 默认挂载 viteAppConfigPlugin

                ├─ 过滤 APP_PUBLIC_*,保留完整键名
                ├─ 生成 window.__UNIBOOT_ADMIN_APP_CONFIG__ = { APP_PUBLIC_API_URL: ... }
                ├─ emit → dist/_app-config-{version}-{hash}.js
                └─ 注入 → index.html <head> 最前面的 <script>


浏览器先执行配置脚本,再加载业务 bundle


@uniboot/app-config → src/config/index.ts 展开 getPublicRuntimeConfig()
阶段命令 / 时机配置从哪来
本地开发pnpm devimport.meta.env.APP_PUBLIC_*(插件不注入文件)
生产构建pnpm build生成 _app-config-*.js 并写入 index.html
线上运行打开已部署页面优先读 window 上的运行时配置

构建产物

在项目根目录执行:

bash
pnpm build

构建成功后,dist 目录会出现类似文件:

text
dist/
├── index.html
├── _app-config-1.0.0-d9fbd877.js   ← 运行时配置(可改)
└── assets/
    └── ...

文件名规则:{fileNamePrefix}-{version}-{hash}.js

来源示例
前缀默认 _app-config(可用选项改)_app-config
version.env 中的 VITE_APP_VERSION(默认)1.0.0
hash配置内容的 MD5 前 8 位;内容变了文件名会变d9fbd877

生成的 JS 内容示意:

js
window.__UNIBOOT_ADMIN_APP_CONFIG__ = {
  APP_PUBLIC_APP_BASE: '/merchant-web',
  APP_PUBLIC_CLIENT_ID: 'koms',
  APP_PUBLIC_REDIRECT_URI: 'https://merchant-sit.klond.com.cn/merchant-web',
  APP_PUBLIC_API_URL: 'https://merchant-sit.klond.com.cn/merchant-api',
  APP_PUBLIC_AUTH_SERVICE_URL: 'https://merchant-sit.klond.com.cn/auth-service',
  APP_PUBLIC_ADMIN_SERVICE_URL:
    'https://merchant-sit.klond.com.cn/admin-service',
}
Object.freeze(window.__UNIBOOT_ADMIN_APP_CONFIG__)
Object.defineProperty(window, '__UNIBOOT_ADMIN_APP_CONFIG__', {
  configurable: false,
  writable: false,
})

文件末尾的两段锁定语句由插件自动追加,不是业务配置的一部分

语句作用
Object.freeze(...)冻结配置对象本身,运行时不能再增删改其字段(例如不能偷偷改 APP_PUBLIC_API_URL
Object.defineProperty(..., { writable: false, configurable: false })锁定 window 上的属性绑定,不能再把该全局键重新赋值或 delete

这样写是为了:配置脚本在 <head> 最早执行后立刻锁死,避免后续第三方脚本或误操作改写运行时配置。运维部署后改配置时,只需改前面的赋值对象里的字符串值;这两段锁定语句不要删——它们在脚本执行时生效,不影响你编辑 dist 文件后再发布。

对应的 index.html 会在 <head> 最前面插入:

html
<script src="/_app-config-1.0.0-d9fbd877.js"></script>

若项目配置了非根路径 base(例如 /merchant-web/),src 会带上该前缀,例如 /merchant-web/_app-config-1.0.0-d9fbd877.js

TIP

使用独立 JS,而不是打进业务 bundle,是为了保证:配置脚本一定在业务代码之前执行,且运维改配置时不必动整包资源。

在业务代码中读取配置

模板已在 src/config/index.ts@uniboot/app-config 封装好读取逻辑,业务侧请统一从 @/config 取值,不要在各处直接读 window

ts
import configs from '@/config'

const request = createHttpClient({
  baseURL: configs.proxy ? '/api' : configs.APP_PUBLIC_API_URL,
  timeout: configs.timeout,
  // ...
})

src/config/index.ts 示意:

ts
import { getPublicRuntimeConfig } from '@uniboot/app-config'

const config = {
  ...getPublicRuntimeConfig(), // 全部 APP_PUBLIC_*
  VITE_APP_TITLE: import.meta.env.VITE_APP_TITLE as string,
  VITE_APP_VERSION: import.meta.env.VITE_APP_VERSION as string,
  timeout: 60 * 1000,
  // ...
}

内部逻辑可以概括为:

  1. 生产环境import.meta.env.PROD === true):getPublicRuntimeConfigwindow.__UNIBOOT_ADMIN_APP_CONFIG__(默认 windowKey 下会回退 __APP_CONFIG__
  2. 开发环境:从 import.meta.env 筛出全部 APP_PUBLIC_*
  3. 配置对象键名与 env / 产物键名一致,例如 configs.APP_PUBLIC_API_URL

部署后如何改配置

假设测试环境和生产环境接口不同,但想共用同一次构建产物:

  1. dist 部署到目标服务器。
  2. 用编辑器或发布脚本打开 dist/_app-config-{version}-{hash}.js
  3. 只改对象里的字符串值,例如:
js
window.__UNIBOOT_ADMIN_APP_CONFIG__ = {
  APP_PUBLIC_APP_BASE: '/merchant-web',
  APP_PUBLIC_CLIENT_ID: 'koms',
  APP_PUBLIC_API_URL: 'https://merchant-prod.example.com/merchant-api', // 改这里
  // ...
}
  1. 保存后强制刷新页面(必要时清 CDN / 浏览器缓存),新配置即生效。

注意

  • 不要删掉 Object.freeze / Object.defineProperty 相关语句。它们只在浏览器执行脚本时生效,用来防止运行时被改写;删掉后配置对象可被任意脚本篡改。
  • 键名是完整APP_PUBLIC_*(例如 APP_PUBLIC_API_URL),不要写成去前缀的 API_URL 或 camelCase 的 apiUrl
  • 若改完内容后重新执行了 pnpm build,hash 可能变化,index.html 里的引用文件名也会更新;部署时请整包一起发布,或同步改 index.html 中的 <script src>

新增一个可动态修改的配置项

按以下步骤即可让「开发可读、生产可改」——无需改 @uniboot/app-config 或插件

1. 在环境文件中增加变量

bash
# .env.development / .env.production
APP_PUBLIC_OTHER_API=https://mock-api.klond.com.cn/other-api

2.(可选)补充 TypeScript 类型

在业务项目中增强类型(包内已有 index signature,不增强也能以 string | undefined 访问):

ts
// src/types/env.d.ts
declare interface AppPublicRuntimeConfig {
  APP_PUBLIC_OTHER_API?: string
}

interface ImportMetaEnv {
  readonly APP_PUBLIC_OTHER_API: string
}

3. 在业务中使用

模板 config...getPublicRuntimeConfig(),无需再改 src/config/index.ts

ts
import configs from '@/config'
// 或:import { getPublicEnv } from '@uniboot/app-config'

console.log(configs.APP_PUBLIC_OTHER_API)
// getPublicEnv('APP_PUBLIC_OTHER_API')

重新 pnpm build 后,新字段会出现在 _app-config-*.js 里,部署后也可继续直接改该文件。

自定义插件选项

多数项目使用默认值即可。若要改 window 键名、环境变量前缀或产物文件名:

推荐(走工厂):

ts
defineApplicationConfig({
  appConfig: {
    windowKey: '__UNIBOOT_ADMIN_APP_CONFIG__',
    envPrefix: 'APP_PUBLIC_',
    fileNamePrefix: '_app-config',
    versionEnvKey: 'VITE_APP_VERSION',
  },
})

或直接挂插件:

ts
viteAppConfigPlugin({
  isBuild,
  windowKey: '__UNIBOOT_ADMIN_APP_CONFIG__',
  envPrefix: 'APP_PUBLIC_',
  fileNamePrefix: '_app-config',
  versionEnvKey: 'VITE_APP_VERSION',
})

TIP

若你修改了 windowKey / envPrefix,请同步:

  1. src/types/global.d.tsWindow 接口上的属性名
  2. getPublicRuntimeConfig({ windowKey, envPrefix }) 传入相同值
  3. Vite envPrefix.env 键名前缀(工厂已默认包含 APP_PUBLIC_

否则构建产物写的是新键,业务代码仍读旧键,生产环境会拿到空配置。

常见问题

开发环境改了 .env.development 没生效?

重启 pnpm dev。Vite 对 env 文件的变更通常需要重启开发服务器。

生产页面接口地址还是旧的?

  1. 确认改的是当前 index.html 引用的那份 _app-config-*.js(看 <script src>)。
  2. 确认 CDN / 浏览器没有缓存旧配置文件。
  3. 确认业务代码走的是 @/config,而不是写死的常量。

import.meta.env.APP_PUBLIC_*undefined

确认使用 defineApplicationConfig(已内置 envPrefix),或手写配置时包含:

ts
envPrefix: ['VITE_', 'APP_PUBLIC_']

只想改标题,不想做成可部署后修改?

VITE_*(例如 VITE_APP_TITLE)。这类值会打进 bundle,改完必须重新构建。

构建时提示 NODE_ENV=production is not supported in the .env file

.env.production / .env.development 中删除 NODE_ENV=... 即可。pnpm build 会自动进入 production 模式。

base / 端口和 .env 对不上?

  • base:工厂用 baseFromEnv: 'APP_PUBLIC_APP_BASE' 时从 env 读取并补尾斜杠。
  • 端口:优先 VITE_PORT;选项 port 只是 env 缺失时的回退。强制端口用 overrides.server.port

API 速查

defineApplicationConfig 选项

见上文「选项说明」。

viteAppConfigPlugin 选项

名称说明类型默认值
isBuild是否启用插件;传 false 时返回 undefined,不注册任何钩子booleantrue
windowKey挂到 window 上的全局变量名string__UNIBOOT_ADMIN_APP_CONFIG__
envPrefix写入运行时配置的环境变量前缀;写入产物时保留完整键名stringAPP_PUBLIC_
fileNamePrefix产物文件名前缀,最终为 {prefix}-{version}-{hash}.jsstring_app-config
versionEnvKey文件名中 version 段对应的 env keystringVITE_APP_VERSION

相关约定

名称说明
APP_PUBLIC_*运行时可配置项;构建时写入 _app-config-*.js(完整键名)
VITE_APP_VERSION默认用于产物文件名中的 version;未设置时回退为 1.0.0
window.__UNIBOOT_ADMIN_APP_CONFIG__插件默认写入的全局对象;产物末尾会 Object.freeze 并锁定属性绑定
window.__APP_CONFIG__兼容旧键名;@uniboot/app-config 在默认 windowKey 下会回退读取
@uniboot/vite-config应用 Vite 工厂:defineApplicationConfig
@uniboot/app-config客户端读取入口:getPublicEnv / getPublicRuntimeConfig
@uniboot/app-config/vite构建插件入口:viteAppConfigPlugin
@/config业务推荐入口;展开运行时配置并附带 VITE_* 与代码常量