引言
Vite 的「零配置可用」是一种体验,而真正让一个项目适应不同团队、不同环境、不同部署拓扑的,是它灵活而分层的配置体系。很多人把 vite.config.ts 当成一份「抄来的样板」,遇到代理失效、环境变量注入失败、生产路径不对时只能靠猜。
本文从 defineConfig 的类型推导讲起,系统拆解 resolve / server / build 三大核心配置块,深入 import.meta.env 的注入机制与 .env 多环境管理,最后给出从配置模板到常见陷阱的完整实践。读懂本文,你将能把配置文件从「黑盒样板」变成「心中有数」。
前置:https://plumephp.com/vite-scaffold-engineering/。需要了解底层实现可配合 https://plumephp.com/frontend-vite-deep-dive/。
目录
- 1. defineConfig 与配置优先级
- 2. resolve:路径解析与别名
- 3. server:开发服务器调优
- 4. build:生产构建配置
- 5. import.meta.env 环境变量机制
- 6. .env 文件与多环境管理
- 7. 配置中的 Node 环境感知
- 8. 常见陷阱与调试
- 9. 总结:配置的分层心智模型
- 延伸阅读
1. defineConfig 与配置优先级
1.1 为什么用 defineConfig 包裹
defineConfig 提供类型推导:包裹后 defineConfig({}) 里的配置对象会对齐完整的 UserConfig 类型,自动补全与校验,也支持条件函数形式:
import { defineConfig } from 'vite'
// 对象形式
export default defineConfig({
base: '/',
plugins: [],
})
// 函数形式:可接收 mode 与 command
export default defineConfig(({ command, mode, isSsrBuild }) => {
return {
base: command === 'serve' ? '/' : '/app/',
}
})
1.2 回调参数
| 参数 | 类型 | 含义 |
|---|---|---|
command | 'serve' / 'build' | 当前是 dev server 还是构建 |
mode | string | 由 --mode 或默认值决定(development/production) |
isSsrBuild | boolean | 是否 SSR 构建 |
1.3 配置加载顺序
命令行选项 (--port 5173)
└─> vite.config 中的配置
└─> 默认值 (合并,不覆盖)
规则:命令行参数的优先级高于配置文件;配置文件高于默认值。合并是浅层合并,嵌套对象需注意覆盖行为。
2. resolve:路径解析与别名
2.1 alias 的完整用法
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
export default defineConfig({
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'~': fileURLToPath(new URL('./src', import.meta.url)),
// 也可以指向字符串
'utils': '/src/utils',
// 精确匹配或子路径匹配
'react-router-dom': 'react-router-dom/es',
},
},
})
2.2 alias 的匹配语义
| 写法 | 匹配范围 |
|---|---|
'@': path | 精确匹配 @ 或以 @/... 开头 |
'foo$': path | $ 结尾仅精确匹配 foo(不匹配 foo/bar) |
find 数组 | 正则匹配 |
2.3 extensions 与 mainFields
export default defineConfig({
resolve: {
// 默认扩展名解析顺序
extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'],
// 解析 package.json 时的字段优先级(默认 browser -> module -> main)
mainFields: ['browser', 'module', 'jsnext:main', 'jsnext'],
},
})
3. server:开发服务器调优
3.1 基础项
export default defineConfig({
server: {
port: 5173,
host: '0.0.0.0', // 允许局域网访问
strictPort: true, // 端口被占用时不自动递增,直接报错
open: true, // 启动自动打开浏览器
cors: true, // 开发期跨域
},
})
3.2 proxy:开发期代理解决跨域
export default defineConfig({
server: {
proxy: {
// 将 /api 请求代理到后端
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
// 路径重写:/api/users -> /users
rewrite: (path) => path.replace(/^\/api/, ''),
},
// WebSocket 代理
'/ws': {
target: 'ws://localhost:3000',
ws: true,
},
// 正则匹配
'/^\/fallback/': {
target: 'http://localhost:9090',
},
},
},
})
3.3 代理的核心价值
- 开发期免 CORS:浏览器访问同源 Vite,Vite 代发请求到后端。
- 路径改写:统一前后端 API 前缀。
- 支持 wss/http2:
ws: true支持 WebSocket。
4. build:生产构建配置
4.1 核心项
export default defineConfig({
build: {
outDir: 'dist',
assetsDir: 'assets', // 静态资源目录
target: 'es2020', // 构建目标(browserslist 语法)
minify: 'esbuild', // 或 'terser'
sourcemap: true, // 生产 sourcemap
chunkSizeWarningLimit: 500, // chunk 大小警告阈值 (kB)
rollupOptions: {
output: {
// 手动分包示例
manualChunks: {
'vendor-react': ['react', 'react-dom'],
'vendor-utils': ['lodash-es'],
},
},
},
},
})
4.2 base:部署路径关键
export default defineConfig({
// 部署在子路径下必须设置,例如 GitHub Pages 的 /repo/
base: '/my-app/',
})
base='/my-app/' -> 产物内所有资源引用变为 /my-app/assets/xxx.js
base='./' -> 相对路径(适合任意目录部署)
4.3 assetsInlineLimit
export default defineConfig({
build: {
// 小于该值的资源转 base64 内联,减少请求数
assetsInlineLimit: 4096, // 4KB
},
})
5. import.meta.env 环境变量机制
5.1 注入模型
.env 文件 -> 被 Vite 加载(dotenv)-> 暴露到 import.meta.env
│
├─> 前缀 VITE_ 的变量 -> 暴露给客户端代码
└─> 非 VITE_ 前缀 -> 仅在 vite.config 中可见(loadEnv)
5.2 内置变量
| 变量 | 含义 |
|---|---|
import.meta.env.MODE | 当前模式(development/production) |
import.meta.env.DEV | 是否开发模式 |
import.meta.env.PROD | 是否生产模式 |
import.meta.env.SSR | 是否 SSR 构建 |
import.meta.env.BASE_URL | base 配置值 |
import.meta.env.PROD | 生产环境布尔值 |
5.3 自定义变量必须在 vite-env.d.ts 声明
// src/vite-env.d.ts
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_APP_TITLE: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
5.4 在代码中使用
// 直接使用,构建时被静态替换
const apiBase = import.meta.env.VITE_API_URL
// 需要 TS 类型则先声明
const title: string = import.meta.env.VITE_APP_TITLE
注意:
import.meta.env是静态替换而非运行时读取——只有字面量.VITE_XXX会被替换,动态访问import.meta.env[key]不会工作。
6. .env 文件与多环境管理
6.1 文件命名与优先级
.env # 所有环境加载
.env.local # 所有环境 + 本地覆盖(不入库)
.env.development # 开发模式
.env.production # 生产模式
.env.staging # 需 --mode staging 显式指定
.env.local.development
优先级(从高到低):.env.local > .env.<mode> > .env。注意 .env.local 永远最高,用于本地个性化配置。
6.2 多环境典型结构
.env # 公共:VITE_APP_NAME=MyApp
.env.development # VITE_API_URL=http://localhost:8080/api
.env.production # VITE_API_URL=https://api.example.com
.env.staging # VITE_API_URL=https://staging-api.example.com
.env.local # 本地调试覆盖(gitignored)
6.3 指定模式启动/构建
# 开发(默认 development)
npm run dev
# 以 staging 模式构建
vite build --mode staging
# 构建时注入的环境变量在代码中被替换
# dist 内 VITE_API_URL=https://staging-api.example.com
6.4 在 vite.config 中读取 env
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
// 此处可读取任意前缀变量(默认前缀 VITE_)
return {
define: {
// 把非 VITE_ 前缀的变量注入到客户端
__APP_VERSION__: JSON.stringify(env.VERSION),
},
}
})
7. 配置中的 Node 环境感知
7.1 用 Node 变量做条件
import { defineConfig } from 'vite'
export default defineConfig(({ command, mode }) => {
const isProd = mode === 'production'
return {
define: {
// 生产才启用某些全局
__DEV__: JSON.stringify(!isProd),
},
plugins: [
// 根据环境启用不同插件
isProd ? productionPlugin() : devPlugin(),
],
build: {
sourcemap: !isProd, // 生产关闭 sourcemap
},
}
})
7.2 环境变量在配置内 vs 配置外
| 位置 | 能访问 | 典型用途 |
|---|---|---|
vite.config.ts | 全部(含无前缀) | 插件配置、define、代理 |
| 客户端代码 | 仅 VITE_ 前缀 | 业务常量、API 地址 |
边界:任何需要保密的密钥(如数据库密码、签名私钥)都不应加 VITE_ 前缀——它会被打包进产物、公开可见。
8. 常见陷阱与调试
8.1 陷阱清单
| 现象 | 原因 | 解决 |
|---|---|---|
| 环境变量是 undefined | 变量无 VITE_ 前缀 | 添加前缀 |
| TS 报 env 不存在 | 未声明 ImportMetaEnv | 补充 vite-env.d.ts |
| 代理不生效 | rewrite 写错 / target 不可达 | 检查 target + changeOrigin |
| 子路径部署资源 404 | 未设置 base | 配置 base 或 ./ |
| 动态 env 访问失效 | import.meta.env[key] 无法替换 | 用字面量访问 |
8.2 调试技巧
// 临时打印完整 env
console.log(loadEnv(mode, process.cwd(), ''))
// 查看最终解析配置
npx vite --debug
8.3 用 –debug 追踪代理
vite --debug | grep proxy
9. 总结:配置的分层心智模型
9.1 一句话框架
resolve(怎么找文件) -> server(怎么开发) -> build(怎么发布) -> env(怎么区分环境)
9.2 核心规则
- 命令行 > 配置文件 > 默认值,浅层合并。
VITE_前缀决定客户端可见性,密钥绝不加前缀。import.meta.env是静态替换,务必用字面量。base决定部署路径,子路径部署必配。- 代理解决开发期跨域,
rewrite控制路径映射。
9.3 自检清单
| 检查项 | 是否掌握 |
|---|---|
| 能说出配置加载优先级 | ☐ |
| 能配置 proxy + rewrite | ☐ |
| 能实现多环境 .env 管理 | ☐ |
| 能理解 base 对产物路径的影响 | ☐ |
| 能解释 VITE_ 前缀边界 | ☐ |
延伸阅读
- https://plumephp.com/vite-scaffold-engineering/ — 脚手架与工程化起步
- https://plumephp.com/vite-build-optimization/ — 生产构建优化
- https://plumephp.com/vite-plugin-development/ — 用插件扩展配置能力
- Vite 配置参考文档 — 全部配置项官方索引
- Vite 环境变量与模式
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。