5月31日 16:17

Vite 环境变量怎么用?为什么只有 VITE_ 前缀能进客户端?

Vite 的环境变量分两类:一类给构建工具和服务端配置用,一类会被注入到浏览器代码里。很多线上事故都出在这里:以为 .env 里的变量只是本地配置,结果把密钥用 VITE_ 开头写进了前端包。记住一句话,凡是能通过 import.meta.env 在客户端读到的值,都应该被当成公开信息。

.env 文件怎么加载

Vite 会按当前 mode 读取环境变量。常见文件有 .env.env.local.env.development.env.production.env.[mode].local。本地覆盖文件通常不要提交,因为它经常放个人端口、测试地址或临时开关。

bash
# .env VITE_APP_TITLE=Admin Console VITE_API_BASE=/api # .env.staging VITE_API_BASE=https://staging-api.example.com # .env.local,不提交 LOCAL_PROXY_TARGET=http://127.0.0.1:7001

客户端代码只能访问以 VITE_ 开头的变量,这是 Vite 的安全边界。这个边界不是为了加密,而是为了避免你无意把 DB_PASSWORDJWT_SECRET 这类服务端变量打进 JS。变量会在构建时被静态替换,所以生产包里的值不会随着服务器环境自动变化。

ts
const apiBase = import.meta.env.VITE_API_BASE const isDev = import.meta.env.DEV const mode = import.meta.env.MODE

内置变量有哪些

import.meta.env.MODE 表示当前模式,默认开发是 development,构建是 productionDEVPROD 是布尔值,适合控制调试面板、mock 逻辑和埋点开关。BASE_URL 来自 base 配置,部署到子目录时很有用。SSR 表示代码是否运行在服务端渲染环境,写同构逻辑时要特别注意。

vite.config 里怎么读取

在配置文件里不能直接依赖客户端的 import.meta.env。需要用 loadEnv(mode, root, prefix) 主动加载,第三个参数决定读取哪些前缀。这里有个取舍:传空字符串可以读到所有变量,方便配置代理;但也更容易误用敏感信息,所以只在配置层使用,不要再塞回客户端。

ts
import { defineConfig, loadEnv } from 'vite' export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), '') return { server: { proxy: { '/api': { target: env.LOCAL_PROXY_TARGET, changeOrigin: true } } } } })

TypeScript 项目还应该补类型,否则变量名写错只能到运行时才发现。

ts
/// <reference types="vite/client" /> interface ImportMetaEnv { readonly VITE_APP_TITLE: string readonly VITE_API_BASE: string } interface ImportMeta { readonly env: ImportMetaEnv }

常见配置边界

环境变量还经常和代理、部署路径、CDN 地址混在一起。接口代理通常只属于开发服务器配置,应该写在 server.proxy,不要让业务代码感知本地代理目标。部署子路径则应该优先配置 base,再通过 import.meta.env.BASE_URL 拼静态资源路径。这样区分以后,开发、预发、生产不会因为一个变量名承担太多含义而互相影响。

还有一个容易忽略的点:.env 文件变更后通常要重启 Vite dev server。因为配置和环境变量是在启动阶段加载的,不是每次模块热更新都重新读取。

如果确实需要运行时切换配置,可以让后端在 HTML 里注入 window.__APP_CONFIG__,或者让前端启动后先请求一个公开配置接口。这样会多一次维护成本,但能避免每个环境都重新打包。边界仍然一样,接口返回的内容也是公开的,不能放密钥。

追问

为什么生产环境改了服务器变量,前端页面没变化?

因为 Vite 的客户端环境变量是在构建时替换的,不是浏览器运行时再去服务器读。你改了容器或机器上的环境变量,但没有重新 build,打出来的 JS 还是旧值。边界是:构建期配置用 Vite env,运行期配置要走接口、HTML 注入或独立配置文件。多租户系统尤其要注意,不能把租户域名这类运行时信息硬编码进包里。

VITE_ 前缀是不是安全机制?

它更像防误伤机制,不是安全机制。加了 VITE_ 的值会进入客户端包,任何人都能在源码或网络资源里看到。取舍是公开配置可以用它,比如页面标题、接口基础路径、Sentry DSN;真正的密钥必须放服务端。踩坑最多的是把第三方 secret 写成 VITE_XXX_SECRET,这等于主动泄露。

mode 和 NODE_ENV 是一回事吗?

不是。mode 决定 Vite 加载哪组 .env.[mode] 文件,NODE_ENV 更多影响依赖库的生产/开发分支。你可以用 vite build --mode staging 生成预发包,但它仍然是生产构建。边界是不要用 MODE !== 'production' 判断是否压缩或是否启用调试,应该优先看 import.meta.env.DEVPROD

环境变量应该怎么做类型和默认值?

类型文件只能告诉 TypeScript “这个变量应该存在”,不能保证运行时真的有值。关键变量最好在启动或构建阶段做校验,缺了就抛错。取舍是简单项目可以直接读,企业项目建议封装一层 env.ts,集中处理默认值、布尔转换和错误提示。布尔值也要小心,.env 读出来都是字符串,"false" 在 JS 里仍然是真值。

框架项目里环境变量放在哪里更合适?

纯前端 Vite 应用可以放在项目根目录的 .env 系列文件里。SSR 框架或 BFF 项目要区分客户端变量和服务端变量,不能为了省事全部加 VITE_。边界是:浏览器需要知道的才进 VITE_,服务器专用的只在服务端读取。CI/CD 里通常提交 .env.example,真实值由流水线或部署平台注入。

标签:Vite