398 lines
11 KiB
Markdown
398 lines
11 KiB
Markdown
# 9.3 环境与构建配置
|
||
|
||
:::tip 概述
|
||
本节说明前端项目的环境变量配置和 Vite 构建配置,包括开发环境和生产环境的差异配置。前端通过 `.env` 文件管理环境变量,Vite 自动根据模式加载对应的 `.env` 文件。
|
||
:::
|
||
|
||
## 环境变量文件
|
||
|
||
```text
|
||
ui/
|
||
├── .env # 所有环境通用配置(端口、标题)
|
||
├── .env.development # 开发环境(pnpm dev,默认 mode)
|
||
├── .env.production # 生产构建(pnpm build)
|
||
├── .env.test # 测试构建(pnpm build:test)
|
||
├── .env.uat # UAT 构建(pnpm build:uat)
|
||
└── .env.dev # dev 模式构建(pnpm build:dev)
|
||
```
|
||
|
||
**加载优先级:** `.env.{mode}` > `.env`(Vite 标准加载规则,可用 `.env.{mode}.local` 做本地覆盖)
|
||
|
||
## 开发环境配置
|
||
|
||
### `.env.development`
|
||
|
||
```bash
|
||
# 开发服务器端口
|
||
VITE_PORT = 8001
|
||
|
||
# 公共基础路径(通常为 /)
|
||
VITE_PUBLIC_PATH = /
|
||
|
||
# 是否启用 Mock 数据(false = 使用真实后端 API)
|
||
VITE_USE_MOCK = false
|
||
|
||
# 是否删除 console(当前项目开发构建同样删除)
|
||
VITE_DROP_CONSOLE = true
|
||
|
||
# 开发代理(JSON 数组格式:[["前缀","目标地址"]])
|
||
VITE_PROXY=[["/api","http://admin.laravel13.elevue/api"]]
|
||
|
||
# API 接口基础路径(留空,由代理处理)
|
||
VITE_GLOB_API_URL =
|
||
|
||
# 上传接口地址(留空,走同域/代理)
|
||
VITE_GLOB_UPLOAD_URL=
|
||
|
||
# 图片前缀地址(留空,按后端返回的地址解析)
|
||
VITE_GLOB_IMG_URL=
|
||
|
||
# 构建压缩方式:none / gzip / brotli
|
||
VITE_BUILD_COMPRESS = 'none'
|
||
|
||
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
|
||
|
||
# API 前缀(所有接口路径的前缀)
|
||
VITE_GLOB_API_URL_PREFIX = /api
|
||
```
|
||
|
||
:::tip 代理目标地址
|
||
`VITE_PROXY` 当前默认指向 Laragon 虚拟主机 `http://admin.laravel13.elevue/api`;使用 `php artisan serve` 时改为直连 `http://localhost:8000/api`(见 [2.2 Laragon 环境搭建](/zh/quickstart/laragon.html))。修改后需重启 `pnpm dev`。
|
||
:::
|
||
|
||
## 生产环境配置
|
||
|
||
### `.env.production`
|
||
|
||
```bash
|
||
# 是否启用 Mock 数据
|
||
VITE_USE_MOCK = false
|
||
|
||
# 公共基础路径(通常为 /,如部署到子目录则设为 /subdir/)
|
||
VITE_PUBLIC_PATH = /
|
||
|
||
# 是否删除 console
|
||
VITE_DROP_CONSOLE = true
|
||
|
||
# API 接口基础路径(留空,由 Nginx 代理处理)
|
||
VITE_GLOB_API_URL =
|
||
|
||
# 上传接口地址(留空,走同域)
|
||
VITE_GLOB_UPLOAD_URL=
|
||
|
||
# 图片前缀地址(留空;跨域部署时配置 CDN/后端域名,与 FILE_DOMAIN_URL 一致)
|
||
VITE_GLOB_IMG_URL=
|
||
|
||
# API 前缀
|
||
VITE_GLOB_API_URL_PREFIX = /api
|
||
|
||
# 构建压缩方式(当前默认 none;可选 gzip / brotli)
|
||
VITE_BUILD_COMPRESS = 'none'
|
||
|
||
# 压缩后是否删除原文件
|
||
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
|
||
```
|
||
|
||
## 环境变量详解
|
||
|
||
### VITE_PORT — 开发服务器端口
|
||
|
||
| 环境 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| 开发 | `8001` | 前端开发服务器端口 |
|
||
| 生产 | 不生效 | 生产环境由 Nginx 提供服务 |
|
||
|
||
### VITE_PUBLIC_PATH — 公共基础路径
|
||
|
||
| 场景 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| 根目录部署 | `/` | 默认值 |
|
||
| 子目录部署 | `/admin/` | 如 `https://example.com/admin/` |
|
||
|
||
### VITE_GLOB_API_URL — API 基础路径
|
||
|
||
| 场景 | 值 | 说明 |
|
||
|------|-----|------|
|
||
| 开发(代理) | 留空 | 由 Vite proxy 转发 |
|
||
| 生产(同域) | 留空 | 由 Nginx 代理 |
|
||
| 生产(跨域) | `https://api.example.com` | 直接请求后端域名(需后端 CORS 放行) |
|
||
|
||
### VITE_PROXY — 开发代理配置
|
||
|
||
```bash
|
||
# 当前 .env.development 中的值(Laragon 虚拟主机)
|
||
VITE_PROXY=[["/api","http://admin.laravel13.elevue/api"]]
|
||
|
||
# 直连 artisan serve 时的写法
|
||
VITE_PROXY=[["/api","http://localhost:8000/api"]]
|
||
```
|
||
|
||
**格式:** JSON 数组,每项为 `[前缀, 目标地址]`
|
||
|
||
**作用:** 将前端 `/api/*` 请求代理到后端地址,解决开发环境跨域问题(代理方式下不依赖后端 CORS 配置)。
|
||
|
||
**多代理示例:**
|
||
|
||
```bash
|
||
VITE_PROXY = [["/api","http://localhost:8000/api"],["/uploads","http://localhost:8000/uploads"]]
|
||
```
|
||
|
||
### VITE_GLOB_API_URL_PREFIX — API 前缀
|
||
|
||
```bash
|
||
VITE_GLOB_API_URL_PREFIX = /api
|
||
```
|
||
|
||
所有 API 请求路径的前缀,与后端路由前缀一致(`bootstrap/app.php` 统一添加 `/api`)。
|
||
|
||
### VITE_GLOB_UPLOAD_URL — 上传接口地址
|
||
|
||
```bash
|
||
VITE_GLOB_UPLOAD_URL=
|
||
```
|
||
|
||
文件上传组件使用的接口地址(实际调用 `/api/upload/uploadFile`)。**当前项目留空**——上传走同域/开发代理,跨域部署时再配置为后端完整地址。
|
||
|
||
### VITE_GLOB_IMG_URL — 图片访问地址
|
||
|
||
```bash
|
||
VITE_GLOB_IMG_URL=
|
||
```
|
||
|
||
用于预览上传的图片,拼接图片的完整 URL。**当前项目留空**(后端 `get_file_url()` 已按 `FILE_DOMAIN_URL` 补全域名);如配置,**必须与后端 `FILE_DOMAIN_URL` 保持一致**,否则同一张图片前后端解析出不同地址。
|
||
|
||
### VITE_DROP_CONSOLE — 删除 console
|
||
|
||
```bash
|
||
# 当前项目开发与生产构建均为 true(删除 console)
|
||
VITE_DROP_CONSOLE = true
|
||
|
||
# 需要调试时可在本地改为 false
|
||
```
|
||
|
||
### VITE_BUILD_COMPRESS — Gzip 压缩
|
||
|
||
```bash
|
||
# 当前默认关闭
|
||
VITE_BUILD_COMPRESS = 'none'
|
||
|
||
# 可选:gzip / brotli(多个用逗号分隔),启用后构建产物生成 .gz 文件,
|
||
# Nginx 配置 gzip_static on 可直接使用
|
||
VITE_BUILD_COMPRESS = 'gzip'
|
||
```
|
||
|
||
## 在代码中访问环境变量
|
||
|
||
```typescript
|
||
// 读取环境变量(VITE_ 前缀的变量才会暴露给客户端)
|
||
const apiUrl = import.meta.env.VITE_GLOB_API_URL;
|
||
const apiPrefix = import.meta.env.VITE_GLOB_API_URL_PREFIX;
|
||
const uploadUrl = import.meta.env.VITE_GLOB_UPLOAD_URL;
|
||
const imgUrl = import.meta.env.VITE_GLOB_IMG_URL;
|
||
|
||
// 完整 API 地址
|
||
const fullApiUrl = apiUrl + apiPrefix; // 如 "/api" 或 "https://api.example.com/api"
|
||
```
|
||
|
||
:::warning VITE_ 前缀
|
||
只有以 `VITE_` 开头的环境变量才会暴露给客户端代码,其他变量(如 `PORT`)不会被访问到。
|
||
:::
|
||
|
||
## Vite 配置
|
||
|
||
### `vite.config.ts`
|
||
|
||
```typescript
|
||
// ui/vite.config.ts(摘自实际代码,节选)
|
||
export default defineConfig(({ mode }) => {
|
||
const env = loadEnv(mode, process.cwd());
|
||
|
||
// VITE_PROXY 容错处理:未配置时使用空代理
|
||
let VITE_PROXY: any = viteEnv.VITE_PROXY;
|
||
if (!VITE_PROXY) {
|
||
VITE_PROXY = '[[]]'; // 默认空代理
|
||
}
|
||
if (typeof VITE_PROXY === 'string') {
|
||
VITE_PROXY = JSON.parse(VITE_PROXY);
|
||
}
|
||
|
||
return {
|
||
server: {
|
||
proxy: createProxy(VITE_PROXY), // 见 build/vite/proxy.ts
|
||
},
|
||
// ... 插件、路径别名等见 ui/vite.config.ts 全文
|
||
};
|
||
});
|
||
|
||
// ui/build/vite/proxy.ts —— 由 [前缀, 目标] 数组生成代理规则
|
||
export function createProxy(list: ProxyList = []) {
|
||
const ret: ProxyTargetList = {};
|
||
for (const [prefix, target] of list) {
|
||
ret[prefix] = {
|
||
target,
|
||
changeOrigin: true,
|
||
ws: true,
|
||
rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''), // 去掉前缀再转发
|
||
};
|
||
}
|
||
return ret;
|
||
}
|
||
```
|
||
|
||
### 代理配置详解
|
||
|
||
```typescript
|
||
// createProxy 为每个 [前缀, 目标] 生成的规则(ui/build/vite/proxy.ts 实际逻辑)
|
||
server: {
|
||
proxy: {
|
||
'/api': {
|
||
target: 'http://localhost:8000/api', // 目标地址(VITE_PROXY 驱动,含 /api 前缀)
|
||
changeOrigin: true, // 修改请求头 Origin
|
||
ws: true, // 支持 WebSocket
|
||
rewrite: (path) => path.replace(new RegExp('^/api'), ''), // 去掉前缀,由目标地址补回
|
||
},
|
||
},
|
||
}
|
||
```
|
||
|
||
**代理效果:**
|
||
|
||
```text
|
||
前端请求: GET /api/user/page
|
||
│
|
||
▼ Vite proxy
|
||
│
|
||
▼ 转发到: http://localhost:8000/api/user/page
|
||
```
|
||
|
||
## 构建命令
|
||
|
||
```bash
|
||
# 开发服务器(热更新)
|
||
pnpm dev
|
||
|
||
# 构建生产版本
|
||
pnpm build
|
||
|
||
# 预览构建结果(本地启动静态服务器)
|
||
pnpm preview
|
||
|
||
# ESLint 检查 / 自动修复
|
||
pnpm lint:eslint
|
||
pnpm lint:eslint --fix
|
||
|
||
# TypeScript 类型检查
|
||
pnpm type:check
|
||
```
|
||
|
||
### 构建产物
|
||
|
||
```text
|
||
ui/dist/
|
||
├── index.html # 入口文件
|
||
├── assets/
|
||
│ ├── index-xxx.js # 主 JS(带 hash)
|
||
│ ├── index-xxx.css # 主 CSS(带 hash)
|
||
│ ├── vue-xxx.js # Vue 相关(分包)
|
||
│ └── element-xxx.js # Element Plus(分包)
|
||
├── favicon.ico
|
||
└── static/ # 静态资源
|
||
```
|
||
|
||
## 部署配置
|
||
|
||
### Nginx 配置
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name admin.example.com;
|
||
|
||
# 前端静态文件
|
||
root /data/www/admin/dist;
|
||
index index.html;
|
||
|
||
# SPA 路由(所有非文件请求都返回 index.html)
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
|
||
# API 代理到后端
|
||
location /api/ {
|
||
proxy_pass http://127.0.0.1:8000/api/;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
}
|
||
|
||
# 静态资源缓存
|
||
location /assets/ {
|
||
expires 30d;
|
||
add_header Cache-Control "public, immutable";
|
||
}
|
||
|
||
# Gzip 压缩
|
||
gzip on;
|
||
gzip_static on; # 优先使用 .gz 文件
|
||
gzip_types text/css application/javascript application/json;
|
||
}
|
||
```
|
||
|
||
## 多环境配置
|
||
|
||
### 测试 / UAT 环境(项目实际提供)
|
||
|
||
```bash
|
||
# .env.test(pnpm build:test)
|
||
VITE_GLOB_API_URL =
|
||
VITE_GLOB_API_URL_PREFIX = /api
|
||
VITE_DROP_CONSOLE = true
|
||
|
||
# .env.uat(pnpm build:uat)同结构,按需调整
|
||
```
|
||
|
||
构建命令(见 `ui/package.json` scripts):
|
||
|
||
```bash
|
||
pnpm build:test # 测试环境构建
|
||
pnpm build:uat # UAT 环境构建
|
||
pnpm build:prod # 生产环境构建
|
||
```
|
||
|
||
### 环境变量对照表
|
||
|
||
| 变量 | 开发 | 测试 | 生产 |
|
||
|------|------|------|------|
|
||
| `VITE_PORT` | 8001 | - | - |
|
||
| `VITE_GLOB_API_URL` | 留空 | 留空 | 留空 |
|
||
| `VITE_GLOB_API_URL_PREFIX` | /api | /api | /api |
|
||
| `VITE_PROXY` | `[["/api","http://admin.laravel13.elevue/api"]]` | - | - |
|
||
| `VITE_GLOB_IMG_URL` | 留空 | 留空 | 留空(按需配置 CDN) |
|
||
| `VITE_DROP_CONSOLE` | true | true | true |
|
||
| `VITE_USE_MOCK` | false | false | false |
|
||
|
||
## 常见问题
|
||
|
||
### 问题 1:开发环境 API 请求 404
|
||
|
||
**原因:** 代理配置错误或后端未启动。
|
||
|
||
**解决:** 检查 `.env.development` 中 `VITE_PROXY` 的目标地址是否正确,`curl http://localhost:8000/api/captcha` 验证后端;改完配置重启 `pnpm dev`。
|
||
|
||
### 问题 2:生产环境 API 请求跨域
|
||
|
||
**原因:** `VITE_GLOB_API_URL` 配置了跨域地址但后端未配置 CORS。
|
||
|
||
**解决:** 推荐留空,由 Nginx 代理处理(同域无跨域问题);确需跨域时在后端 `.env` 配置 `CORS_ALLOW_ORIGIN`(见 [5.5 跨域资源共享](/zh/core/cors.html))。
|
||
|
||
### 问题 3:图片不显示
|
||
|
||
**原因:** `VITE_GLOB_IMG_URL` 与后端 `FILE_DOMAIN_URL` 不一致。
|
||
|
||
**解决:** 两者保持一致;改后端配置需 `php artisan config:clear`。
|
||
|
||
### 问题 4:构建后体积过大
|
||
|
||
**原因:** 未启用 gzip 压缩或未分包。
|
||
|
||
**解决:** 启用 `VITE_BUILD_COMPRESS = gzip`,配置 `manualChunks` 分包。
|