跳过正文
  1. 文章/

深度解析:Vite 配置中 `define` 的使用场景与实战指南

·1572 字·4 分钟·
hujiacheng
作者
hujiacheng
Front-end Developer / Strive To Become Better
目录

📌 1. 核心原理:什么是静态替换?
#

在 Vite (底层依赖 esbuild 和 Rollup) 中,define 的本质是全局常量静态替换。它不是在浏览器里挂载一个 window.XXX 变量,而是在编译打包阶段,像“查找并替换”一样,直接把代码里的标识符替换成具体的值。

🔍 编译前后的直观对比: 假设我们在 vite.config.ts 中配置了 __APP_VERSION__: '"20260227.1200.abc1234"'

  • 开发者的源码 (编译前):
console.log("当前系统版本:", __APP_VERSION__);
  • Vite 打包后的产物 (编译后):
// 变量名直接消失,变成了硬编码的字符串字面量
console.log("当前系统版本:", "20260227.1200.abc1234");

🛠️ 2. 典型配置项解析 (以 Vue-i18n 为例)
#

在许多使用 Vue 3 + vue-i18n 的项目中,常会看到如下配置,它们主要用于优化第三方库的构建体积:

define: {
  __VUE_I18N_FULL_INSTALL__: true,  // 包含完整运行时和编译器
  __VUE_I18N_LEGACY_API__: false,   // 禁用 Vue2 语法,触发 Tree-shaking 减小体积
  __INTLIFY_PROD_DEVTOOLS__: false, // 生产环境关闭 Devtools 面板
}

🚀 3. 高阶实战:高频发版项目的自动化版本号注入
#

对于使用 CI/CD 流水线敏捷开发、高频发版的项目,每次手动修改 package.json 极易引发冲突。最佳实践是:构建时根据时间戳和 Git Hash 自动生成版本号,再通过 define 注入到前端环境。

步骤 1:编写版本号自动生成脚本
#

在项目根目录创建 scripts/generate-version.js,该脚本会在打包前运行,提取时间和当前 Git 短 Hash 生成唯一标识。

#!/usr/bin/env node
/**
 * 版本号生成脚本
 * 格式: YYYYMMDD.HHmm.git短hash (例如: 20260119.1830.abc1234)
 */
const { execSync } = require("child_process");
const { writeFileSync } = require("fs");
const { join } = require("path");

const rootDir = join(__dirname, "..");
const versionFile = join(rootDir, ".version");

function getTimestamp() {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, "0");
  const day = String(now.getDate()).padStart(2, "0");
  const hour = String(now.getHours()).padStart(2, "0");
  const minute = String(now.getMinutes()).padStart(2, "0");
  return `${year}${month}${day}.${hour}${minute}`;
}

function getGitHash() {
  try {
    return execSync("git rev-parse --short HEAD", {
      cwd: rootDir,
      encoding: "utf-8",
    }).trim();
  } catch {
    return "local";
  }
}

function main() {
  const version = `${getTimestamp()}.${getGitHash()}`;
  writeFileSync(versionFile, version, "utf-8");
  console.log(`✅ 版本号已生成: ${version} 并写入 .version 文件`);
}

main();

步骤 2:配置 package.json 执行构建钩子
#

让项目在执行 build 之前,先自动跑一遍上面的生成脚本:

{
  "scripts": {
    "prebuild": "node scripts/generate-version.js",
    "build": "vite build"
  }
}

步骤 3:编写版本读取工具
#

由于 Vite 配置是基于 ESM 的,我们需要一个简单的工具来读取刚才生成的 .version 文件内容:

// src/utils/readVersion.mjs
import { readFileSync, existsSync } from "fs";
import { join } from "path";

export function readVersion() {
  const versionFile = join(process.cwd(), ".version");
  if (existsSync(versionFile)) {
    return readFileSync(versionFile, "utf-8").trim();
  }
  return "dev";
}

步骤 4:在 vite.config.ts 中完成 define 注入
#

将读取到的版本号字符串,通过 define 正式注入!

// vite.config.ts
import { defineConfig } from "vite";
import { readVersion } from "./src/utils/readVersion.mjs";

export default defineConfig({
  define: {
    // 注入业务变量
    __APP_VERSION__: JSON.stringify(readVersion()),
  },
});

💻 4. 业务代码使用与 TS 类型补全
#

4.1 消除 TypeScript 报错 (前置工作)
#

因为 __APP_VERSION__ 是静态替换的,TS 编译器并不知道它的存在。必须在 src/vite-env.d.ts 中进行全局声明:

// src/vite-env.d.ts
/// <reference types="vite/client" />

declare const __VUE_I18N_FULL_INSTALL__: boolean;
declare const __VUE_I18N_LEGACY_API__: boolean;
declare const __INTLIFY_PROD_DEVTOOLS__: boolean;
declare const __APP_VERSION__: string;

4.2 在 Vue 组件 / Sentry 中直接使用
#

无需 import,任何地方都能直接用:

<template>
  <div class="footer">
    <span class="version-tag">系统当前版本: v{{ currentVersion }}</span>
  </div>
</template>

<script setup lang="ts">
  import { ref } from "vue";
  const currentVersion = ref(__APP_VERSION__);
</script>
// Sentry 监控初始化
Sentry.init({
  dsn: "your-dsn",
  release: __APP_VERSION__, // 报错时精准关联到具体的 Git commit 和时间
});

💡 5. 进阶认知:define vs import.meta.env
#

特性defineimport.meta.env (.env 文件)
设计初衷替换任意全局常量、第三方库的特性开关。注入项目自身的业务环境变量 (如 API 接口地址)。
使用方式__APP_VERSION__import.meta.env.VITE_API_URL
数据类型支持复杂类型 (布尔值、对象),替换为 JS 表达式。默认全部解析为字符串 (String)。
是否暴露可以任意命名,无限制。只有以 VITE_ 开头的变量才会打包进源码。

⚠️ 6. 避坑指南:为什么字符串必须用 JSON.stringify
#

在配置 define 时,如果你的值是一个字符串,绝对不能直接赋值纯字符串,必须使用 JSON.stringify() 包裹。

这是因为 define 执行的是“纯代码文本替换”:

  • 错误写法:
define: {
  __APP_VERSION__: "1.0.0";
}

编译后果: 代码中的 const v = __APP_VERSION__ 会被直接替换成没有引号的代码 const v = 1.0.0。这会引发 JS 语法错误 SyntaxError: Unexpected number

  • 正确写法:
define: {
  __APP_VERSION__: JSON.stringify("1.0.0");
}

编译后果: JSON.stringify 会将其转换为带有双引号的字符串 '"1.0.0"'。代码中的 const v = __APP_VERSION__ 会被替换成 const v = "1.0.0",这才是合法且符合预期的 JS 字符串赋值。

(注:布尔值如 true/false 本身就是合法的 JS 表达式,因此不需要使用 stringify 进行转义。)

相关文章