如果想了解更多与 Vue3 有关的内容,可以查看 Vue3 专栏: Vue3

1. Vite中的环境变量和模式

我们先来看一下Vite官方对环境变量和模式的解释:https://cn.vitejs.dev/guide/env-and-mode

https://cn.vitejs.dev/guide/env-and-mode

Vite 在特殊的 import.meta.env 对象下暴露了一些常量。这些常量在开发阶段被定义为全局变量,并在构建阶段被静态替换,以使树摇(tree-shaking)更有效。

在这里插入图片描述

1.1 内置常量

一些内置常量在所有情况下都可用:

变量名类型说明
import.meta.env.MODE{string}应用运行的模式
import.meta.env.BASE_URL{string}部署应用时的基本 URL。由 base 配置项决定
import.meta.env.PROD{boolean}应用是否运行在生产环境(使用 NODE_ENV='production' 运行开发服务器或构建应用时使用 NODE_ENV='production'
import.meta.env.DEV{boolean}应用是否运行在开发环境(永远与 import.meta.env.PROD 相反)
import.meta.env.SSR{boolean}应用是否运行在 server 上

1.2 环境变量

Vite 自动将环境变量暴露在 import.meta.env 对象下,作为字符串

为了防止意外地将一些环境变量泄漏到客户端,只有以 VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码


例如下面这些环境变量:

VITE_SOME_KEY=123
DB_PASSWORD=foobar

只有 VITE_SOME_KEY 会被暴露为 import.meta.env.VITE_SOME_KEY 提供给客户端源码,而 DB_PASSWORD 则不会

console.log(import.meta.env.VITE_SOME_KEY) // "123"
console.log(import.meta.env.DB_PASSWORD) // undefined

如上所示,VITE_SOME_KEY 是一个数字,但在解析时会返回一个字符串。布尔类型的环境变量也会发生同样的情况。在代码中使用时,请确保转换为所需的类型

2. 如何使用Vite配置环境变量

2.1 在项目的根目录下创建一个名为.env的文件

在项目的根目录下创建一个名为.env的文件

在这里插入图片描述

2.2 编写.env文件

.env 文件使用 KEY=VALUE 的格式,每行一个变量,不需要加引号,变量命名以VITE_开头

VITE_BACKEND_URL=https://api.default.com

2.3 在代码中使用环境变量

在 Vue 组件中使用环境变量

const backendUrl = import.meta.env.VITE_BACKEND_URL

2.4 示例代码

HomeView.vue

<template>
  <div class="home-container">
    <div class="content-wrapper">
      <!-- 上半部分:内置常量 -->
      <div class="section-card">
        <div class="section-header">
          <h2 class="section-title">内置常量</h2>
          <p class="section-subtitle">Vite 内置的环境变量</p>
        </div>
        <div class="constants-grid">
          <div class="constant-item">
            <span class="constant-label">MODE</span>
            <span class="constant-value">{{ mode }}</span>
          </div>
          <div class="constant-item">
            <span class="constant-label">BASE_URL</span>
            <span class="constant-value">{{ baseURL }}</span>
          </div>
          <div class="constant-item">
            <span class="constant-label">PROD</span>
            <span class="constant-value boolean">{{ prod }}</span>
          </div>
          <div class="constant-item">
            <span class="constant-label">DEV</span>
            <span class="constant-value boolean">{{ dev }}</span>
          </div>
          <div class="constant-item">
            <span class="constant-label">SSR</span>
            <span class="constant-value boolean">{{ ssr }}</span>
          </div>
        </div>
      </div>

      <!-- 下半部分:环境变量 -->
      <div class="section-card">
        <div class="section-header">
          <h2 class="section-title">环境变量</h2>
          <p class="section-subtitle">自定义的环境变量配置</p>
        </div>
        <div class="env-grid">
          <div class="env-item">
            <span class="env-label">VITE_BACKEND_URL</span>
            <span class="env-value">{{ backendUrl }}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
const mode = import.meta.env.MODE
const baseURL = import.meta.env.BASE_URL
const prod = import.meta.env.PROD
const dev = import.meta.env.DEV
const ssr = import.meta.env.SSR
const backendUrl = import.meta.env.VITE_BACKEND_URL
</script>

<style scoped>
.home-container {
  min-height: 100vh;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  padding: 40px 20px;
  box-sizing: border-box;
}

.content-wrapper {
  max-width: 800px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.section-card {
  background: rgba(255, 255, 255, 0.95);
  border-radius: 24px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
  backdrop-filter: blur(10px);
  padding: 40px;
  transition: all 0.3s ease;
  border: 1px solid rgba(255, 255, 255, 0.2);
  animation: slideUp 0.6s ease-out;
}

.section-card:hover {
  transform: translateY(-5px);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}

.section-header {
  text-align: center;
  margin-bottom: 30px;
}

.section-title {
  font-size: 2.5rem;
  font-weight: 800;
  color: #2d3748;
  margin: 0 0 10px 0;
  letter-spacing: -1px;
}

.section-subtitle {
  font-size: 1.1rem;
  color: #718096;
  margin: 0;
  font-weight: 400;
}

.constants-grid,
.env-grid {
  display: grid;
  gap: 20px;
}

.constants-grid {
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

.env-grid {
  grid-template-columns: 1fr;
  max-width: 500px;
  margin: 0 auto;
}

.constant-item,
.env-item {
  background: rgba(102, 126, 234, 0.08);
  border: 1px solid rgba(102, 126, 234, 0.2);
  border-radius: 16px;
  padding: 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: all 0.3s ease;
}

.constant-item:hover,
.env-item:hover {
  background: rgba(102, 126, 234, 0.12);
  transform: translateY(-2px);
}

.constant-label,
.env-label {
  font-weight: 700;
  color: #4c51bf;
  font-size: 1rem;
  font-family: 'Courier New', monospace;
}

.constant-value,
.env-value {
  font-weight: 600;
  color: #2d3748;
  font-size: 1.1rem;
  font-family: 'Courier New', monospace;
  background: rgba(255, 255, 255, 0.8);
  padding: 8px 16px;
  border-radius: 8px;
  border: 1px solid rgba(0, 0, 0, 0.1);
}

.constant-value.boolean {
  color: #38a169;
  background: rgba(56, 161, 105, 0.1);
  border-color: rgba(56, 161, 105, 0.3);
}

.env-value {
  color: #667eea;
  background: rgba(102, 126, 234, 0.1);
  border-color: rgba(102, 126, 234, 0.3);
}

/* 响应式设计 */
@media (max-width: 768px) {
  .home-container {
    padding: 20px 15px;
  }

  .section-card {
    padding: 30px 20px;
  }

  .section-title {
    font-size: 2rem;
  }

  .section-subtitle {
    font-size: 1rem;
  }

  .constants-grid {
    grid-template-columns: 1fr;
  }

  .constant-item,
  .env-item {
    flex-direction: column;
    text-align: center;
    gap: 10px;
  }

  .constant-label,
  .env-label {
    font-size: 0.9rem;
  }

  .constant-value,
  .env-value {
    font-size: 1rem;
  }
}

@media (max-width: 480px) {
  .section-title {
    font-size: 1.75rem;
  }
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
</style>

开发环境

在这里插入图片描述

生产环境(将项目打包后将文件上传到 Nginx 或 Tomcat 等 Web 服务器上)

在这里插入图片描述

3. 不同.env文件的优先级

当多个 .env 文件定义了相同的变量时,优先级高的文件会覆盖优先级低的文件中的同名变量

3.1 优先级顺序(.env、.env.local、.env.[mode]、.env.[mode].local)

✅ 最高优先级的是 .env.[mode].local,它会覆盖前面所有文件中同名的变量

以下是 Vite 的 .env 文件加载优先级顺序(从低到高)

  1. .env
  2. .env.local
  3. .env.[mode]
  4. .env.[mode].local

假设当前模式是 production,Vite 会按以下顺序加载并合并环境变量:

  1. .env → 基础变量
  2. .env.local → 本地通用覆盖
  3. .env.production → 生产环境专属变量
  4. .env.production.local → 生产环境本地覆盖(最高优先级)

如果多个文件都定义了 VITE_API_URL,最终生效的是 .env.production.local 中的值

3.2 示例说明

下面我们通过一个具体例子来说明 Vite 中 .env 文件的加载优先级


🎯 场景设定

假设项目当前运行在 **production **模式下,执行命令:

vite build

Vite 默认使用 production 模式,因此会加载与 production 相关的 .env 文件


假设项目根目录下有以下 4 个文件:

.env
.env.local
.env.production
.env.production.local

📄 各文件内容如下:

.env(所有环境通用)

VITE_API_URL=https://api.env.com

.env.local(本地通用覆盖)

VITE_API_URL=https://api.local.com
VITE_DEBUG=false

.env.production(生产环境专用)

VITE_API_URL=https://api.production.com

.env.production.local(生产环境本地覆盖,最高优先级)

VITE_API_URL=https://api.production-local.com

✅ 最终生效的环境变量(在 import.meta.env 中)

import.meta.env.VITE_API_URL      // "https://api.production-local.com"

⚠️ 注意

虽然 .env.[mode].local 优先级最高,但它属于全局本地覆盖,建议只用于本地调试,不要提交到版本控制

3.3 已存在的环境变量与.env文件中定义的环境变量的优先级

Vite 执行时已经存在的环境变量(例如操作系统级别的环境变量)有最高的优先级,不会被 .env 类文件覆盖


如果添加操作系统级别的环境变量后显示的还是.env文件中的环境变量,可能是因为 IDE 有缓存,重启 IDE 后再次启动项目就可以了

4. 使用Vite配置环境变量的安全注意事项

在这里插入图片描述

安全注意事项

  • .env.*.local 文件应是本地的,可以包含敏感变量。你应该将 *.local 添加到你的 .gitignore 中,以避免它们被 git 检入
  • 由于任何暴露给 Vite 源码的变量最终都将出现在客户端包中,VITE_* 变量应该不包含任何敏感信息

🔐 哪些变量是安全的?哪些是危险的?


✅ 可以暴露的(安全):

这些信息本来就是前端运行所必需的,即使被看到也无妨

  • 前端需要调用的 API 地址(如 VITE_API_URL
  • 用于配置 UI 的变量(如 VITE_APP_NAME, VITE_THEME_COLOR
  • 第三方服务的公开密钥(如 Google Maps 的 前端可用 API Key

❌ 绝对不能暴露的(危险):

这些必须只存在于服务器端,永远不要出现在前端代码中!

  • 后端 API 的认证密钥(如 JWT secret)
  • 数据库连接字符串
  • 支付接口的私钥(如 Stripe Secret Key)
  • 任何需要保密的身份凭证(tokens, passwords, private keys)

5. 补充:树摇(tree-shaking)

5.1 🔍 什么是“树摇”(Tree Shaking)?

树摇(Tree Shaking) 是一种在打包过程中移除未使用代码(dead code elimination)的优化技术


树摇的名字来源于这样一个比喻:想象一棵树,你摇一摇它,那些枯掉的、没用的叶子(未使用的代码)就会掉下来,剩下的只有有用的枝叶(真正被使用的代码)

5.2 🌲 树摇是怎么工作的?

树摇主要依赖于 ES6 模块系统(ESM)的静态结构,也就是说:

  • importexport静态声明(不能在运行时动态改变)
  • 打包工具(如 Vite、Rollup、Webpack)可以在打包前分析哪些模块被导入了,哪些函数/变量根本没被用到

✅ 举个例子:

// utils.js
export const fetchUser = () => { /* ... */ }
export const fetchProduct = () => { /* ... */ }
export const debugLog = () => { console.log("Debug!") }

// main.js
import { fetchUser } from './utils.js'
fetchUser()

在打包时,打包工具发现:

  • fetchProduct 没被用
  • debugLog 没被用

👉 所以它们会被“摇掉”——不会被打包进最终的生产代码中,从而减小包体积

5.3 🔄 Vite 如何利用树摇优化 import.meta.env

Vite 在构建时会把 import.meta.env 中的变量静态替换成实际值,例如:

if (import.meta.env.DEV) {
  console.log('这是开发环境')
}

会被构建工具替换为:

if (true) {  // 因为当前是开发环境
  console.log('这是开发环境')
}

然后,构建工具(如 Rollup)会进一步优化:

  • 如果某个 if (false) 的分支,整个代码块都会被移除
  • 这样对应的代码就不会被打包进去

✅ 举个实际例子:

if (import.meta.env.MODE === 'development') {
  console.log('开发调试信息')
}

在生产构建中,MODE'production',所以这行代码会被替换为:

if ('production' === 'development') {  // false
  console.log('开发调试信息')
}

然后构建工具发现这是一个永远不成立的 if (false),于是整个 console.log 代码块被完全移除

👉 这就是树摇的威力:不仅删掉了没用的函数,还能删掉根据环境判断的无用逻辑

5.4 ✅ 树摇的好处

好处说明
📦 包体积更小没用的代码不会被打包,加载更快
⚡ 性能更好浏览器下载和解析的代码更少
🧹 代码更干净生产环境不会包含开发调试代码

5.5 ❗ 注意:树摇只对 ESM 有效

  • ✅ 使用 import / export(ES 模块) → 支持树摇
  • ❌ 使用 require / module.exports(CommonJS) → 通常不支持树摇(因为是动态的)

所以现代前端项目推荐使用 ES 模块语法

5.6 ✅ 对树摇的总结

树摇(Tree Shaking) = 打包时自动删除未使用的代码

Vite 利用树摇来:

  • 移除没有用到的函数、模块
  • 移除基于 import.meta.env 的无效分支(如开发日志)
  • 让最终的客户端代码更轻量、更高效

6. 补充:Vite和Vue3的关系

Vite 是一个“通用”工具,而 Vue 3 是一个“框架”

  • Vite 是构建工具:它不关心你用的是什么框架。你可以用 Vite 来开发 React、Preact、Svelte、Lit 等任何现代前端框架项目。它负责处理文件编译、服务启动、热更新、生产打包等工程化问题
  • Vue 3 是前端框架:它提供了一套用于构建用户界面的核心库和编程范式(响应式、组件化、路由、状态管理等)。它本身不关心你用什么工具来打包和构建它

Vite 最初就是为了更好地开发 Vue 3 而创建的。尤雨溪在设计 Vue 3 的 Composition API 和 SFC(Single File Component,单文件组件)时,希望有一个能充分发挥其潜力的构建工具,因此,Vite 对 Vue 提供了一等公民的支持:

  • 开箱即用:使用 npm create vue@latest(官方脚手架)创建的 Vue 3 项目,默认就是使用 Vite 作为构建工具。你不需要任何复杂配置,就能立即获得极速的开发体验
  • 内置对 .vue 文件的支持:Vite 内部集成了 @vitejs/plugin-vue,这个插件负责在开发时即时编译 .vue 文件,使其能被浏览器通过 ESM 加载
  • HMR 针对 Vue 优化:Vite 的 HMR 策略与 Vue 的组件更新机制完美结合,确保组件更新时的高效和状态保留

  • ESM(ECMAScript Modules,ECMAScript 模块):一种官方的、标准化的 JavaScript 模块系统,让你可以在一个 JavaScript 文件中导入(import)和导出(export)代码,从而实现代码的拆分和复用
  • HMR(Hot Module Replacement,热模块替换):一种开发时功能,允许你在修改代码后,将发生变化的模块实时更新到浏览器中,而无需刷新整个页面
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐