WinddSnow

Vite-Configuration-Environment-Variables-Proxy-Build-Optimization

字数统计: 3.8k阅读时长: 16 min
2026/07/31

第79课:Vite 配置与环境变量——vite.config.ts、环境变量、代理配置、构建优化

上一课我们学习了 Vite 的冷启动原理、HMR 机制和插件系统。本节课将深入 Vite 的配置文件——vite.config.ts 中那些控制开发体验和生产输出的关键选项。我们将逐一拆解 环境变量.env 文件)的加载与类型安全使用、开发服务器代理server.proxy)解决跨域问题的配置技巧、以及生产构建优化(代码分割、资源内联、压缩策略)的最佳实践。掌握这些配置,你就能将 Vite 从一个“能跑的开发服务器”调校为“高性能的生产构建流水线”。


1. 环境变量与模式

Vite 使用 dotenv.env 文件中加载环境变量,并通过 import.meta.env 暴露给客户端代码。与 Webpack 的 process.env 不同,Vite 的环境变量是静态替换的——在构建时直接内联为字符串字面量,而非运行时从全局对象读取。这一设计使得环境变量可以被 Tree Shaking。

1.1 .env 文件的命名与加载优先级

Vite 按以下顺序加载环境变量文件,后加载的覆盖先加载的同名变量

文件名 加载时机 典型用途
.env 所有模式都加载 所有环境的公共变量
.env.local 所有模式都加载,应被 .gitignore 忽略 本地私密配置(本地 API 密钥等)
.env.[mode] 仅在指定模式下加载(如 .env.production 特定模式的公共变量
.env.[mode].local 仅在指定模式下加载,应被 git 忽略 特定模式的本地私密配置

[mode] 由 Vite 的 --mode 参数决定,默认为 developmentvite)、productionvite build)。vite devvite 默认使用 development 模式。

1
2
3
4
5
6
7
8
9
# .env(所有环境共享)
VITE_APP_TITLE=我的应用
VITE_API_BASE_URL=https://api.example.com

# .env.development(仅开发环境)
VITE_API_BASE_URL=http://localhost:3000/api

# .env.production(仅生产环境)
VITE_API_BASE_URL=https://api.example.com

变量命名规则:只有以 VITE_ 开头的变量才会暴露给客户端代码(import.meta.env.VITE_XXX)。这是 Vite 的安全策略——防止意外将敏感变量(如数据库密码)泄漏到浏览器。服务端变量应使用其他前缀或完全不通过 Vite 管理。

1.2 在代码中使用环境变量

Vite 为所有以 VITE_ 开头的变量生成类型定义,可通过 import.meta.env 访问:

1
2
3
4
5
// src/config.ts
export const config = {
apiBaseUrl: import.meta.env.VITE_API_BASE_URL as string,
appTitle: import.meta.env.VITE_APP_TITLE as string,
};

内建变量(无需声明,始终可用):

变量名 说明 示例值
import.meta.env.MODE 当前模式字符串 'development'
import.meta.env.DEV 是否为开发环境(boolean true
import.meta.env.PROD 是否为生产环境(boolean false
import.meta.env.SSR 是否为服务端渲染(boolean false
import.meta.env.BASE_URL 部署的公共基础路径(来自 base 配置) '/'
1
2
3
if (import.meta.env.DEV) {
console.log('当前为开发环境,启用调试日志');
}

1.3 为环境变量生成 TypeScript 类型

通过创建 src/env.d.ts(或 src/vite-env.d.ts,Vite 模板默认生成)来扩展 ImportMetaEnv 接口,获得类型安全和智能提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/env.d.ts
/// <reference types="vite/client" />

interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string;
readonly VITE_APP_TITLE: string;
// 可选变量
readonly VITE_GA_ID?: string;
}

interface ImportMeta {
readonly env: ImportMetaEnv;
}

添加后,编辑器会在你输入 import.meta.env.VITE_ 时自动提示所有声明的变量。


2. 开发服务器代理配置

在开发环境中,前端通常运行在 localhost:5173,而后端 API 运行在 localhost:3000。浏览器的同源策略会阻止跨域请求。Vite 通过 server.proxy 配置提供开箱即用的开发代理——将特定路径的请求转发到后端服务器,绕过跨域限制。

2.1 基础代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
server: {
port: 4000,
proxy: {
// 将所有 /api 开头的请求代理到后端
'/api': {
target: 'http://localhost:3000',
changeOrigin: true, // 修改请求头的 origin 为目标 URL
// 可选:重写路径(去掉 /api 前缀)
// rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
});

前端代码中直接使用相对路径发起请求:

1
2
3
const response = await fetch('/api/users');
// 开发环境:实际请求 http://localhost:3000/api/users
// 生产环境:直接请求 https://yoursite.com/api/users(需后端或 Nginx 处理)

2.2 多路径代理与 WebSocket 代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
'/ws': {
target: 'ws://localhost:3000',
ws: true, // 启用 WebSocket 代理
},
'/uploads': {
target: 'http://localhost:3000',
changeOrigin: true,
}
}
}
});

2.3 代理配置的 configure 函数

当需要动态匹配路径或访问 Node.js 的 HTTP 模块时,使用 configure 回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
configure: (proxy) => {
proxy.on('error', (err) => {
console.error('代理错误:', err);
});
proxy.on('proxyReq', (proxyReq, req) => {
console.log('代理请求:', req.method, req.url);
});
proxy.on('proxyRes', (proxyRes, req) => {
console.log('代理响应:', proxyRes.statusCode, req.url);
});
}
}
}
}
});

3. 生产构建优化

Vite 的生产构建基于 Rollup,通过 build 配置项控制打包行为。以下是最关键的几个优化维度。

3.1 代码分割(Code Splitting)

默认情况下,Vite 会自动提取共享模块到独立的 chunk 中。你可以通过 manualChunks 显式控制哪些依赖打包在一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// 将 Vue 生态单独拆包
'vue-vendor': ['vue', 'vue-router', 'pinia'],
// 将 UI 库单独拆包
'ui-vendor': ['element-plus'],
// 将工具库单独拆包
'utils-vendor': ['lodash-es', 'dayjs', 'axios'],
}
}
}
}
});

manualChunks 的最佳实践

  • 框架核心(Vue、React)单独拆包——这些库很少变动,可长期缓存。
  • 大型 UI 库(Ant Design、Element Plus)单独拆包——体积大,独立缓存收益高。
  • 稳定工具库(lodash、dayjs)单独拆包——几乎不更新,缓存时间最长。
  • 不要为小库或业务代码创建过多 chunk——过多的 HTTP 请求会抵消缓存收益。

也可以使用函数形式动态分包:

1
2
3
4
5
6
7
8
9
10
11
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('vue') || id.includes('vue-router')) {
return 'vue-vendor';
}
if (id.includes('element-plus')) {
return 'ui-vendor';
}
return 'vendor'; // 其余第三方库
}
}

3.2 资源内联与阈值控制

Vite 默认将小于 4KB 的资源内联为 base64,减少 HTTP 请求数。可通过 build.assetsInlineLimit 调整阈值。

1
2
3
4
5
export default defineConfig({
build: {
assetsInlineLimit: 8192, // 8KB 以下的资源内联为 base64(默认 4096 = 4KB)
}
});

设置为 0 则完全禁用资源内联,所有资源都作为独立文件引用。

3.3 CSS 代码分割与处理

Vite 默认将 CSS 按 chunk 分割(与 JS chunk 一一对应),并通过 <link> 标签注入。可以通过 build.cssCodeSplit 控制此行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default defineConfig({
build: {
cssCodeSplit: false, // 所有 CSS 合并为一个文件(适合小型项目)
},
css: {
// CSS 压缩选项(传递给 esbuild 或 postcss)
postcss: './postcss.config.js',
// 预处理器全局注入
preprocessorOptions: {
scss: {
additionalData: '@use "@/styles/variables.scss" as *;'
}
}
}
});

3.4 压缩配置

Vite 使用 esbuild 压缩 JavaScript,使用 esbuildlightningcss 压缩 CSS。可通过 build.minifybuild.cssMinify 分别控制。

1
2
3
4
5
6
7
export default defineConfig({
build: {
minify: 'esbuild', // 'esbuild' | 'terser' | false
cssMinify: 'lightningcss', // 'esbuild' | 'lightningcss' | false
target: 'es2022', // 设置浏览器兼容目标
}
});
  • **'esbuild'**(默认):极速压缩,适合大多数项目。
  • **'terser'**:更激进的压缩(如移除 console.log),但速度较慢。适合对产物体积极度敏感的场景。
  • **false**:不压缩(调试用)。

3.5 构建产物分析

使用 rollup-plugin-visualizer 可视化分析打包产物的体积构成,快速定位优化目标。

1
npm install -D rollup-plugin-visualizer
1
2
3
4
5
6
7
8
9
10
11
12
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
plugins: [
visualizer({
open: true, // 构建完成后自动打开浏览器
gzipSize: true, // 显示 gzip 压缩后大小
brotliSize: true, // 显示 brotli 压缩后大小
filename: 'dist/stats.html'
})
]
});

构建后在 dist/stats.html 中生成交互式 TreeMap,清晰展示每个模块在最终 bundle 中的占比。

3.6 基础路径与静态资源处理

base 配置决定了应用的部署路径前缀,对于部署到 CDN 或子目录至关重要。

1
2
3
4
export default defineConfig({
base: '/my-app/', // 部署在 https://example.com/my-app/ 下
// base: 'https://cdn.example.com/assets/', // 资源托管在 CDN
});

base 影响所有资源路径的生成——HTML 中的 <script>、CSS 中的背景图、动态导入的 chunk 路径,都会被加上该前缀。默认值为 '/'(根路径)。


4. 综合实战:一个完整的 Vite 配置

以下配置整合了环境变量类型声明、代理、分包和构建优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig(({ mode }) => {
const isProd = mode === 'production';

return {
plugins: [
vue(),
isProd && visualizer({ gzipSize: true, filename: 'dist/stats.html' }),
],

resolve: {
alias: {
'@': resolve(__dirname, 'src'),
},
},

server: {
port: 4000,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
},

build: {
target: 'es2022',
cssCodeSplit: true,
assetsInlineLimit: 6144, // 6KB
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
},
},
},
},
};
});

课后练习

一、概念自测(选择题 / 填空题)

  1. (单选) Vite 中,哪些环境变量会被暴露给客户端代码?
    A. 所有 .env 文件中声明的变量。
    B. 只有以 VITE_ 开头的变量。
    C. 所有不以 SECRET_ 开头的变量。
    D. 只有 MODEDEVPROD 三个变量。

  2. (单选) server.proxy 配置的 changeOrigin: true 的作用是?
    A. 修改请求的目标 URL。
    B. 修改请求头的 Origin 字段为目标 URL,解决后端校验问题。
    C. 重写请求路径。
    D. 启用 WebSocket 代理。

  3. (填空) 在 Vite 中,要限制小于 8KB 的图片资源被内联为 base64,应配置 build.______8192

  4. (多选) 以下哪些是 Vite 生产构建优化的合理策略?
    A. 通过 manualChunks 将框架核心代码拆分为独立 chunk。
    B. 设置 cssCodeSplit: false 将所有 CSS 合并为一个文件。
    C. 使用 rollup-plugin-visualizer 分析打包体积。
    D. 将所有 node_modules 中的依赖打包进同一个 chunk 以减少请求数。

二、AI 编程任务:编写面向 AI 的提示词

场景:你需要为一个已有的 Vite + React 项目添加以下配置:

  • 配置三个环境变量:VITE_API_BASE_URL(开发环境和生产环境值不同)、VITE_APP_NAME(所有环境共享)、VITE_ENABLE_MOCK(仅开发环境,布尔类型)。
  • 为环境变量生成 TypeScript 类型声明(src/env.d.ts)。
  • 配置开发服务器代理:将 /api 转发到 http://localhost:8080/uploads 转发到 http://localhost:9000
  • 在生产构建中将 reactreact-dom 拆分为独立的 react-vendor chunk,将 antd 拆分为 ui-vendor chunk。

任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成所有需要的配置文件(.env.env.development.env.productionsrc/env.d.tsvite.config.ts)。提示词中需明确指定各环境变量的用途、类型和代理规则。

三、Agent 模式下的提示词示例

你是一个资深前端开发 Agent。请为一个 Vite + React 项目配置环境变量、代理和生产优化。需要创建/修改以下文件:

  1. .env:声明 VITE_APP_NAME=MyReactApp
  2. .env.development:声明 VITE_API_BASE_URL=http://localhost:8080/apiVITE_ENABLE_MOCK=true
  3. .env.production:声明 VITE_API_BASE_URL=https://api.myapp.com
  4. src/env.d.ts:扩展 ImportMetaEnv 接口,为上述三个变量添加类型声明(stringstringboolean),添加 JSDoc 注释。
  5. vite.config.ts
    • 引入 @vitejs/plugin-react
    • server.proxy 配置 /apihttp://localhost:8080/uploadshttp://localhost:9000,均设置 changeOrigin: true
    • build.rollupOptions.output.manualChunks 中将 reactreact-dom 拆分为 react-vendorantd 拆分为 ui-vendor
    • 添加注释说明每个配置的用途。
      确保所有文件可直接使用。完成后列出所有文件内容。

四、面试真题与参考答案

题目(美团前端面试题):

Vite 的环境变量与 Webpack 的 process.env 有何不同?Vite 为什么要以 VITE_ 为前缀来过滤暴露给客户端的变量?此外,请说明 Vite 的 server.proxy 配置在生产环境是否仍然生效,以及为什么。

参考答案

Vite 环境变量与 Webpack 的区别:Vite 使用 import.meta.env 静态替换,变量在构建时就被内联为字符串字面量,而不是像 Webpack 的 process.env 那样在运行时从全局对象读取。这意味着 Vite 的环境变量可以被 Tree Shaking(未使用的变量会被删除),且无法在运行时动态修改。Webpack 通过 DefinePlugin 也支持静态替换,但社区约定俗成使用 process.env.XXX

VITE_ 前缀的安全策略:Vite 只将 VITE_ 前缀的变量暴露给客户端,这是为了防止开发者意外将敏感服务端变量(如数据库密码、API 秘钥)通过 .env 文件泄漏到浏览器代码中。由于 Vite 的静态替换机制会将变量的值直接硬编码到打包产物中,任何在代码中引用的变量值都可以被用户看到。如果不加前缀过滤,.env 中所有变量都可能被打包进客户端代码,造成严重的安全漏洞。

server.proxy 在生产环境不生效server.proxy 仅作用于 Vite 的开发服务器vite dev)。在生产环境中,前端代码被构建为静态文件(HTML/CSS/JS),通常部署在 Nginx、CDN 或静态托管服务上,不再经过 Vite 开发服务器。生产环境的代理需要由反向代理(Nginx、Caddy)或后端网关处理。


课后练习答案

一、概念自测答案

  1. B

    • 解析:Vite 仅将 VITE_ 开头的变量暴露给客户端,以防止敏感信息泄漏。
  2. B

    • 解析:changeOrigin: true 将请求头的 Origin 字段改为目标 URL,避免后端因 Origin 不匹配而拒绝请求。
  3. assetsInlineLimit

    • 解析:build.assetsInlineLimit 控制资源内联为 base64 的阈值(字节)。
  4. A、B、C

    • 解析:D 错误——将所有依赖打成一个巨大 chunk 会丧失缓存优势,首次加载缓慢。合理的策略是按更新频率拆分多个 vendor chunk。

二、AI 编程任务参考答案(提示词示例)

示例提示词
“请为一个 Vite + React 项目生成环境变量和构建配置。要求:

  • 创建 .envVITE_APP_NAME=MyReactApp)、.env.developmentVITE_API_BASE_URL=http://localhost:8080/apiVITE_ENABLE_MOCK=true)、.env.productionVITE_API_BASE_URL=https://api.myapp.com)。
  • 创建 src/env.d.ts,为三个变量添加类型声明(stringstringboolean),添加 JSDoc。
  • 修改 vite.config.ts:引入 @vitejs/plugin-reactserver.proxy 配置 /apihttp://localhost:8080/uploadshttp://localhost:9000build.manualChunksreact/react-dom 拆为 react-vendorantd 拆为 ui-vendor
  • 直接输出所有文件内容。”
CATALOG
  1. 1. 第79课:Vite 配置与环境变量——vite.config.ts、环境变量、代理配置、构建优化
    1. 1.1. 1. 环境变量与模式
      1. 1.1.1. 1.1 .env 文件的命名与加载优先级
      2. 1.1.2. 1.2 在代码中使用环境变量
      3. 1.1.3. 1.3 为环境变量生成 TypeScript 类型
    2. 1.2. 2. 开发服务器代理配置
      1. 1.2.1. 2.1 基础代理
      2. 1.2.2. 2.2 多路径代理与 WebSocket 代理
      3. 1.2.3. 2.3 代理配置的 configure 函数
    3. 1.3. 3. 生产构建优化
      1. 1.3.1. 3.1 代码分割(Code Splitting)
      2. 1.3.2. 3.2 资源内联与阈值控制
      3. 1.3.3. 3.3 CSS 代码分割与处理
      4. 1.3.4. 3.4 压缩配置
      5. 1.3.5. 3.5 构建产物分析
      6. 1.3.6. 3.6 基础路径与静态资源处理
    4. 1.4. 4. 综合实战:一个完整的 Vite 配置
    5. 1.5. 课后练习
      1. 1.5.1. 一、概念自测(选择题 / 填空题)
      2. 1.5.2. 二、AI 编程任务:编写面向 AI 的提示词
      3. 1.5.3. 三、Agent 模式下的提示词示例
      4. 1.5.4. 四、面试真题与参考答案
    6. 1.6. 课后练习答案
      1. 1.6.1. 一、概念自测答案
      2. 1.6.2. 二、AI 编程任务参考答案(提示词示例)