配置
模板通过两类环境变量管理配置:构建期固化的 VITE_*,以及生产环境可部署后修改的 APP_PUBLIC_*。
| 能力 | 包 | 职责 |
|---|---|---|
| Vite 应用配置工厂 | @uniboot/vite-config | defineApplicationConfig:统一 Vue / 插件 / base / chunks;默认内置运行时配置插件 |
| 运行时配置(构建) | @uniboot/app-config/vite | viteAppConfigPlugin:生产构建写出 _app-config-*.js |
| 运行时配置(读取) | @uniboot/app-config | getPublicRuntimeConfig / getPublicEnv:按完整键名读取 |
模板推荐:vite.config.ts 用 @uniboot/vite-config,业务侧从 @/config 读配置(内部用 @uniboot/app-config)。
环境变量配置
项目的环境变量配置位于应用目录下的 .env、.env.development、.env.production。
规则与 Vite Env Variables and Modes 一致。格式如下:
.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.env。defineApplicationConfig已设置envPrefix: ['VITE_', 'APP_PUBLIC_'],因此开发时也可以:tsconsole.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决定。
环境配置说明
# 项目名称(展示用,构建期固化)
VITE_APP_TITLE=UNIBOOT ADMIN
# 项目版本(同时用于配置文件名中的 version)
VITE_APP_VERSION=1.0.0# 启动端口(优先于 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-serviceAPP_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_ID | OAuth 客户端 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) |
另有代码常量(如 timeout、authMethod、urlPrefix、proxy、httpHeaders)写在 src/config/index.ts,不走环境变量。
Vite 配置工厂(@uniboot/vite-config)
模板的 vite.config.ts 使用 defineApplicationConfig,把常见 Vite 基线收进工厂,业务只保留差异项(路径、dedupe、端口回退等)。
安装
模板一般已内置。新项目:
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 模板一致:
// 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_'] |
base | baseFromEnv 从 env 读取并归一化尾斜杠;否则用 base(默认 /) |
| 端口 | 优先 VITE_PORT;未设置时用选项 port(默认 3000) |
resolve.alias | @ → srcAlias(默认 cwd/src) |
resolve.dedupe | 默认 ['vue'],可覆盖 |
build | cssMinify: 'esbuild'、按包名 manualChunks、过滤 INVALID_ANNOTATION |
define | Vue 生产常用开关(Options API / hydration 等) |
选项说明
| 选项 | 说明 |
|---|---|
port | 未设置 VITE_PORT 时的回退端口,默认 3000;有 VITE_PORT 时以 env 为准。强制固定端口请用 overrides.server.port |
base | Vite base;与 baseFromEnv 同时存在时以后者为准 |
baseFromEnv | 从该 env key 读 base(如 APP_PUBLIC_APP_BASE),并做尾斜杠归一化 |
srcAlias | @ 别名绝对路径;默认 process.cwd()/src |
svgIconDirs | 有值才启用 vite-plugin-svg-icons |
svgSymbolId | 默认 local-icon-[dir]-[name] |
dedupe | resolve.dedupe;默认 ['vue'] |
autoImport / components | 与内置选项浅合并 |
appConfig | 传给 viteAppConfigPlugin;传 false 关闭运行时配置插件 |
plugins | 追加到内置插件之后 |
overrides | 与默认配置做 mergeConfig |
覆盖与扩展示例
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 单独引入)的做法是:
- 开发时:不生成额外文件,继续用
.env.development+import.meta.env,本地改完刷新即可。 - 生产构建时:把所有
APP_PUBLIC_*抽成独立的_app-config-{version}-{hash}.js,并自动插入index.html的<head>最前面。 - 部署后:运维只需改 dist 里这份配置文件,刷新页面即可生效,无需重新
pnpm build。
单独挂插件(不用工厂时)
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可强制关闭。
工作原理
.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 dev | import.meta.env.APP_PUBLIC_*(插件不注入文件) |
| 生产构建 | pnpm build | 生成 _app-config-*.js 并写入 index.html |
| 线上运行 | 打开已部署页面 | 优先读 window 上的运行时配置 |
构建产物
在项目根目录执行:
pnpm build构建成功后,dist 目录会出现类似文件:
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 内容示意:
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> 最前面插入:
<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。
import configs from '@/config'
const request = createHttpClient({
baseURL: configs.proxy ? '/api' : configs.APP_PUBLIC_API_URL,
timeout: configs.timeout,
// ...
})src/config/index.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,
// ...
}内部逻辑可以概括为:
- 生产环境(
import.meta.env.PROD === true):getPublicRuntimeConfig读window.__UNIBOOT_ADMIN_APP_CONFIG__(默认windowKey下会回退__APP_CONFIG__) - 开发环境:从
import.meta.env筛出全部APP_PUBLIC_* - 配置对象键名与 env / 产物键名一致,例如
configs.APP_PUBLIC_API_URL
部署后如何改配置
假设测试环境和生产环境接口不同,但想共用同一次构建产物:
- 把
dist部署到目标服务器。 - 用编辑器或发布脚本打开
dist/_app-config-{version}-{hash}.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', // 改这里
// ...
}- 保存后强制刷新页面(必要时清 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. 在环境文件中增加变量
# .env.development / .env.production
APP_PUBLIC_OTHER_API=https://mock-api.klond.com.cn/other-api2.(可选)补充 TypeScript 类型
在业务项目中增强类型(包内已有 index signature,不增强也能以 string | undefined 访问):
// 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:
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 键名、环境变量前缀或产物文件名:
推荐(走工厂):
defineApplicationConfig({
appConfig: {
windowKey: '__UNIBOOT_ADMIN_APP_CONFIG__',
envPrefix: 'APP_PUBLIC_',
fileNamePrefix: '_app-config',
versionEnvKey: 'VITE_APP_VERSION',
},
})或直接挂插件:
viteAppConfigPlugin({
isBuild,
windowKey: '__UNIBOOT_ADMIN_APP_CONFIG__',
envPrefix: 'APP_PUBLIC_',
fileNamePrefix: '_app-config',
versionEnvKey: 'VITE_APP_VERSION',
})TIP
若你修改了 windowKey / envPrefix,请同步:
src/types/global.d.ts里Window接口上的属性名getPublicRuntimeConfig({ windowKey, envPrefix })传入相同值- Vite
envPrefix与.env键名前缀(工厂已默认包含APP_PUBLIC_)
否则构建产物写的是新键,业务代码仍读旧键,生产环境会拿到空配置。
常见问题
开发环境改了 .env.development 没生效?
重启 pnpm dev。Vite 对 env 文件的变更通常需要重启开发服务器。
生产页面接口地址还是旧的?
- 确认改的是当前
index.html引用的那份_app-config-*.js(看<script src>)。 - 确认 CDN / 浏览器没有缓存旧配置文件。
- 确认业务代码走的是
@/config,而不是写死的常量。
import.meta.env.APP_PUBLIC_* 是 undefined?
确认使用 defineApplicationConfig(已内置 envPrefix),或手写配置时包含:
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,不注册任何钩子 | boolean | true |
| windowKey | 挂到 window 上的全局变量名 | string | __UNIBOOT_ADMIN_APP_CONFIG__ |
| envPrefix | 写入运行时配置的环境变量前缀;写入产物时保留完整键名 | string | APP_PUBLIC_ |
| fileNamePrefix | 产物文件名前缀,最终为 {prefix}-{version}-{hash}.js | string | _app-config |
| versionEnvKey | 文件名中 version 段对应的 env key | string | VITE_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_* 与代码常量 |