Vite 环境变量与运行时配置最佳实践:从 .env 到一份包跑多环境

在前端项目开发中,我们经常需要针对不同环境(开发/测试/预发/生产)切换 API 地址或应用配置等。Vite 提供了 .env 文件机制在构建时注入环境变量到 import.meta.env,但构建后的产物配置是静态的、不可再修改

在实际生产部署中,常常需要“一份包跑多个环境”,例如相同构建产物要部署到测试/预发/生产,这就需要 运行时配置覆盖 来实现灵活性。 .env 与 vite.config.js组件,就能实现 开发方便 + 部署灵活 的最佳实践方案

.env 文件加载规则

Vite 会根据 mode 加载对应的 .env 文件,并按顺序合并。

  • .env  所有情况下都会加载
  • .env.local  所有情况下都会加载,但会被 git 忽略
  • .env.[mode]  只在指定模式下加载
  • .env.[mode].local  只在指定模式下加载,但会被 git 忽略

加载顺序(后覆盖前):

.env → .env.local → .env.[mode] → .env.[mode].local

Vite 总是会加载 .env 和 .env.local 文件,除此之外还会加载模式特定的 .env.[mode] 文件。在模式特定文件中声明的变量优先级高于通用文件中的变量,但仅在 .env 或 .env.local 中定义的变量仍然可以在环境中使用

另外,Vite 执行时已经存在的环境变量有最高的优先级,不会被 .env 类文件覆盖。例如当运行 VITE_SOME_KEY=123 vite build 的时候。

.env 类文件会在 Vite 启动一开始时被加载,而改动会在重启服务器后生效。

与命令的关系

{
  "scripts": {
    "dev": "vite", // 默认 mode=development
       "build": "vite build", // 默认 mode=production
       "build:staging": "vite build --mode staging", // 使用 .env.staging
       "preview": "vite preview" // 预览 dist 产物
  }
}

对应关系如下:

  • npm run dev → .env.development
  • npm run build → .env.production
  • npm run build:staging → .env.staging
  • npm run preview → 仅预览打包产物(不会重新加载 .env

环境变量命名规则

必须以 VITE_ 开头才会注入前端

VITE_API_BASE_URL="https://api.example.com"
  • 以 VITE_ 开头的变量才会注入到 import.meta.env
  • 不以 VITE_ 开头的变量仍可写在 .env,但只能在 vite.config.js 中通过 loadEnv 读取

注意:不要把私密信息(token、密码)放在 VITE_ 变量里,会暴露到前端。

所有值必须是字符串

const enabled = import.meta.env.VITE_FEATURE_ENABLED === 'true'
const timeout = Number(import.meta.env.VITE_TIMEOUT_MS || '5000')

如需使用对象:

VITE_FLAGS='{"newUI":true,"abTest":0.3}'

// 获取方式
const flags = JSON.parse(import.meta.env.VITE_FLAGS || '{}')

允许在 .env 中引用已有变量,避免重复:

VITE_API_HOST="https://api.example.com"
VITE_API_VERSION="v1"
VITE_API_BASE_URL="${VITE_API_HOST}/${VITE_API_VERSION}"

结果:VITE_API_BASE_URL=https://api.example.com/v1

内置变量

Vite 提供一些内置变量:

import.meta.env.MODE // "development" | "production" | "staging"
import.meta.env.DEV // true | false
import.meta.env.PROD // true | false
import.meta.env.BASE_URL // 部署时的基础路径

在 vite.config.js 中读取 loadEnv

Vite 提供了 loadEnv(mode, root, prefix) 方法来读取 .env 文件中的变量。

import {
  defineConfig, loadEnv
} from 'vite'
export default defineConfig(({ mode
}) => {
  const env = loadEnv(mode, process.cwd(), '') // 读取全部变量
  console.log(env) // 打印所有环境变量
  return {
    server: {
      proxy: {
        '/api': {
          target: env.VITE_API_PROXY_TARGET,
          changeOrigin: true,
          rewrite: (p) => p.replace(/^\/api/, ''),
        },
      },
    },
  }
})

参数说明

  • mode:当前运行模式,例如 development、production、staging。Vite 会根据这个值去加载对应的 .env.[mode] 文件。
  • root:项目根目录路径。一般写 process.cwd(),表示当前 Node.js 进程的工作目录。这告诉 Vite 从哪里开始查找 .env 文件。
  • prefix:用于过滤变量前缀。默认是 'VITE_',只会返回 VITE_ 开头的变量。如果写成 ''(空字符串),则返回所有 .env 文件中的变量,包括非 VITE_ 的。

示例

// .env.development
NODE_ENV=development
APP_SECRET=123456 # 不会注入客户端
VITE_API_BASE_URL=http://localhost:3000/api

// vite.config.js
export default defineConfig(({ mode
}) => {
  const env = loadEnv(mode, process.cwd(), '')
  console.log(env.NODE_ENV) // "development"
  console.log(env.APP_SECRET) // "123456"
  console.log(env.VITE_API_BASE_URL) // "http://localhost:3000/api"
  return {
    define: {
      __APP_SECRET__: JSON.stringify(env.APP_SECRET), // 可选择性注入
    }
  }
})

自定义暴露前缀

除了默认的 VITE_,你还可以通过 vite.config.js 中的 envPrefix 配置,允许其它前缀的变量也暴露到客户端代码中:

export default defineConfig({
  envPrefix: ['VITE_', 'APP_']
})

这样,在 .env 文件中定义:

APP_TITLE="我的应用"
VITE_API_BASE_URL="https://api.example.com"

就可以在前端代码里直接访问:

console.log(import.meta.env.APP_TITLE) // "我的应用"
console.log(import.meta.env.VITE_API_BASE_URL) // "https://api.example.com"

envPrefix vs loadEnv 的区别

它们看起来相似,但作用范围不同。

envPrefix(vite.config.js 配置项)

  • 控制哪些变量可以被注入到 客户端代码(import.meta.env) 中。
  • 默认只允许 VITE_ 前缀的变量暴露给前端。
  • 修改 envPrefix 可以增加其它前缀,例如 APP_

影响:运行时前端能不能访问。

loadEnv(mode, root, prefix)(函数参数)

用于在 vite.config.js 里读取 .env 文件。

prefix 参数决定返回的变量前缀过滤:

  • 'VITE_' → 只返回 VITE_ 开头的变量。
  • ''(空字符串) → 返回所有变量(包括非 VITE_ 的)。

影响:配置阶段你能拿到哪些变量。

// .env.development
SECRET_KEY=123456
APP_TITLE="我的应用"
VITE_API_BASE_URL="http://localhost:3000/api"

// vite.config.js
import {
  defineConfig, loadEnv
} from 'vite'
export default defineConfig(({ mode
}) => {
  const env = loadEnv(mode, process.cwd(), '') // 拿到全部变量
  console.log(env.SECRET_KEY) // 123456
  console.log(env.APP_TITLE) // 我的应用
  console.log(env.VITE_API_BASE_URL) // http://localhost:3000/api
  return {
    envPrefix: ['VITE_', 'APP_'] // 允许 APP_ 前缀变量暴露到前端
  }
})

// 前端使用
console.log(import.meta.env.VITE_API_BASE_URL) // ✅ 可访问
console.log(import.meta.env.APP_TITLE) // ✅ 可访问
console.log(import.meta.env.SECRET_KEY) // ❌ undefined (没有被暴露)

总结

  • loadEnv(..., prefix) → 影响 vite.config.js 构建阶段能读到哪些变量。
  • envPrefix → 影响 import.meta.env 暴露到客户端的变量范围。

两者并不冲突,而是可以配合使用。

  • 开发阶段可以读取全部变量(loadEnv(mode, root, ''))。
  • 只想暴露部分变量给前端(通过 envPrefix 控制)。

TypeScript 的智能提示

默认情况下,Vite 在 vite/client.d.ts 中为 import.meta.env 提供了类型定义。随着在 .env[mode] 文件中自定义了越来越多的环境变量,你可能想要在代码中获取这些以 VITE_ 为前缀的用户自定义环境变量的 TypeScript 智能提示。

要想做到这一点,你可以在 src 目录下创建一个 vite-env.d.ts 文件,接着按下面这样增加 ImportMetaEnv 的定义:

/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string
  // 更多环境变量...
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

如果你的代码依赖于浏览器环境的类型,比如 DOM 和 WebWorker,你可以在 tsconfig.json 中修改 lib 字段来获取类型支持。

{
  "lib": ["WebWorker"]
}

在 `vite-env.d.ts` 文件中使用 `/// <reference types="vite/client" />` 的目的是引入 Vite 的类型定义。这条指令告诉 TypeScript 解析 Vite 提供的类型,包括对 `import.meta` 和其他 Vite 特性的支持。

Clipboard_Screenshot_1761191178

Clipboard_Screenshot_1761191287

HTML 环境变量替换

Vite 还支持在 HTML 文件中替换环境变量。import.meta.env 中的任何属性都可以通过特殊的 %CONST_NAME% 语法在 HTML 文件中使用:

<h1>Vite is running in %MODE%</h1>
<p>Using data from %VITE_API_URL%</p>

如果环境变量在 import.meta.env 中不存在,比如不存在的 %NON_EXISTENT%,则会将被忽略而不被替换,这与 JS 中的 import.meta.env.NON_EXISTENT 不同,JS 中会被替换为 undefined

NODE_ENV 和 模式

在某些情况下,若想在 vite build 时运行不同的模式来渲染不同的标题,你可以通过传递 --mode 选项标志来覆盖命令使用的默认模式。例如,如果你想在 staging (预发布)模式下构建应用:

vite build --mode staging

需要注意的是,NODE_ENVprocess.env.NODE_ENV)和模式是两个不同的概念。以下是不同命令如何影响 NODE_ENV 和模式:

CommandNODE_ENVMode
vite build "production" "production"
vite build --mode development "production" "development"
NODE_ENV=development vite build "development" "production"
NODE_ENV=development vite build --mode development "development" "development"

NODE_ENV 和模式的不同值也会反映在相应的 import.meta.env 属性上:

Commandimport.meta.env.PRODimport.meta.env.DEV
NODE_ENV=production true false
NODE_ENV=development false true
NODE_ENV=other false true

.env 文件中的 NODE_ENV

NODE_ENV=... 可以在命令中设置,也可以在 .env 文件中设置。

如果在 .env.[mode] 文件中指定了 NODE_ENV,则可以使用模式来控制其值。不过,NODE_ENV 和模式仍然是两个不同的概念。

命令中使用 NODE_ENV=... 的主要好处是,它允许 Vite 提前检测到该值。这也使你能够在 Vite 配置中读取 process.env.NODE_ENV,因为 Vite 只有在解析配置之后才能加载环境变量文件。 

在 Vite 中,`mode` 和 `NODE_ENV` 都可以用来指定不同的环境配置,但它们有不同的用途。

  • `mode`:用于指定 Vite 的运行模式(如 `development`、`production`、`test` 等),影响 Vite 的行为和配置。
  • `NODE_ENV`:通常用于表示 Node.js 环境(如 `development`、`production`),在很多库和框架中用于条件判断。

在 Vite 中,`mode` 会覆盖 `NODE_ENV`,但 `NODE_ENV` 仍然可以被用作一些通用工具或库的环境判断。总的来说,`mode` 在 Vite 中更为重要。

posted @ 2025-10-18 23:35  李小菜丶  阅读(260)  评论(0)    收藏  举报