解释 Vue CLI 中的环境变量和模式 (`.env` 文件) 在源码中的处理方式,以及它们如何影响 Webpack 的构建。

各位观众老爷,大家好!今天咱们聊聊 Vue CLI 里那些神神秘秘的 .env 文件,以及它们如何影响你的 Webpack 构建。别担心,咱们不搞那些玄而又玄的概念,用最接地气的方式,把它们扒个底朝天!

一、.env 文件:你的秘密武器库

想象一下,你的项目里有一些配置信息,比如 API 的地址、数据库的密码等等,这些东西你肯定不想直接写死在代码里,因为这样很不安全,也很不方便修改。这时候,.env 文件就派上用场了。

.env 文件就是一个简单的文本文件,里面存储着键值对,就像这样:

VUE_APP_API_URL=https://api.example.com
VUE_APP_DEBUG=true
DATABASE_PASSWORD=supersecret

注意:

  • 所有的变量名都应该以 VUE_APP_ 开头,这是 Vue CLI 强制的,不然 Webpack 不会理你。当然, NODE_ENV, BASE_URL 和 publicPath 除外。
  • 变量的值可以是字符串、数字、布尔值等等。
  • .env 文件应该被添加到 .gitignore 文件里,避免泄露敏感信息。

二、模式(Modes):不同环境下的配置

有时候,你需要在不同的环境下使用不同的配置。比如,在开发环境中使用测试 API,在生产环境中使用正式 API。这时候,就可以使用模式(Modes)了。

Vue CLI 默认支持三种模式:

  • development:开发环境
  • production:生产环境
  • test:测试环境

你也可以自定义模式,比如 staging

不同的模式对应着不同的 .env 文件:

  • .env:所有模式都会加载的默认配置文件
  • .env.development:开发环境下的配置文件
  • .env.production:生产环境下的配置文件
  • .env.test:测试环境下的配置文件
  • .env.staging:自定义环境下的配置文件

当你在不同的模式下运行项目时,Vue CLI 会自动加载对应的 .env 文件,并将这些变量注入到 process.env 对象中。

举个例子,如果你运行 vue-cli-service serve --mode staging,Vue CLI 会依次加载以下文件:

  1. .env
  2. .env.staging
  3. .env.local
  4. .env.staging.local

注意,后面的文件会覆盖前面的文件,.local 文件优先于非 .local 文件。

三、源码中的处理方式:Webpack 的魔法

那么,Vue CLI 是如何在源码中处理这些 .env 文件的呢?其实,这主要是靠 Webpack 的一个插件:dotenv-webpack

当 Vue CLI 启动时,它会读取 .env 文件,然后使用 dotenv-webpack 插件将这些变量注入到 process.env 对象中。这样,你就可以在代码中通过 process.env.VUE_APP_API_URL 来访问 API 的地址了。

让我们深入到 Vue CLI 的源码中,看看它是怎么做的。

首先,在 vue-cli-servicelib/Service.js 文件中,你会看到类似这样的代码:

const dotenv = require('dotenv');
const dotenvExpand = require('dotenv-expand');
const fs = require('fs');
const path = require('path');

module.exports = class Service {
  // ...

  loadEnv() {
    const basePath = path.resolve(this.context, '.env');
    const localPath = `${basePath}.local`;

    const load = (envPath) => {
      try {
        const env = dotenv.config({ path: envPath, debug: process.env.DEBUG });
        dotenvExpand(env);
        console.log(`Loaded env from ${envPath}`)
      } catch (e) {
        console.error(`Failed to load env from ${envPath}`, e)
      }
    };

    load(basePath);

    // important: .env.local is loaded after .env, so that .env.local
    // can overwrite .env variables
    if (fs.existsSync(localPath)) {
      load(localPath);
    }

    if (process.env.NODE_ENV !== 'test') {
      const mode = this.mode;
      const modePath = `${basePath}.${mode}`;
      if (fs.existsSync(modePath)) {
        load(modePath);
      }

      const modeLocalPath = `${modePath}.local`;
      if (fs.existsSync(modeLocalPath)) {
        load(modeLocalPath);
      }
    }
  }

  resolveWebpackConfig(rawConfig) {
    // ...

    rawConfig.plugins.push(
      new webpack.DefinePlugin({
        'process.env': this.genDefineObject(process.env),
        'process.browser': !!isBrowser
      })
    )

    return rawConfig
  }

  genDefineObject(obj) {
    const res = {}
    for (const key in obj) {
      res[key] = JSON.stringify(obj[key])
    }
    return res
  }

  // ...
}

这段代码做了以下几件事:

  1. 加载 .env 文件: 使用 dotenv.config() 方法加载 .env 文件,并将变量存储到 process.env 对象中。
  2. 处理 .local 文件: 如果存在 .env.local 文件,则加载它,并且它会覆盖 .env 文件中的同名变量。
  3. 处理模式文件: 根据当前的模式(NODE_ENV),加载对应的 .env.mode.env.mode.local 文件,并且它们会覆盖之前的变量。
  4. 注入到 process.env 使用 webpack.DefinePlugin 插件,将 process.env 对象中的变量注入到 Webpack 构建过程中。这样,你就可以在代码中通过 process.env.VUE_APP_API_URL 来访问 API 的地址了。

四、.env 文件如何影响 Webpack 构建

.env 文件通过 webpack.DefinePlugin 插件影响 Webpack 构建。DefinePlugin 允许你在编译时创建全局常量。这意味着你可以使用 .env 文件中的值来配置你的应用,而无需在代码中硬编码这些值。

例如,假设你的 .env 文件中定义了以下变量:

VUE_APP_API_URL=https://api.example.com
VUE_APP_DEBUG=true

然后,你可以在你的 Vue 组件中使用这些变量:

<template>
  <div>
    <p>API URL: {{ apiUrl }}</p>
    <p>Debug Mode: {{ debugMode }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      apiUrl: process.env.VUE_APP_API_URL,
      debugMode: process.env.VUE_APP_DEBUG,
    };
  },
};
</script>

当 Webpack 构建你的应用时,它会将 process.env.VUE_APP_API_URL 替换为 "https://api.example.com",将 process.env.VUE_APP_DEBUG 替换为 "true"。这样,你的应用就可以在运行时访问这些配置信息了。

五、一些注意事项

  • 变量名必须以 VUE_APP_ 开头: 这是 Vue CLI 强制的,不然 Webpack 不会理你。
  • 不要在 .env 文件中存储敏感信息: 比如数据库密码、API 密钥等等。这些信息应该存储在更安全的地方,比如环境变量或者密钥管理系统。
  • 使用不同的 .env 文件来管理不同环境下的配置: 这样可以避免在不同的环境中使用错误的配置。
  • 重启 vue-cli-service 每次修改 .env 文件后,都需要重启 vue-cli-service 才能使修改生效。

六、.env 文件的加载优先级

Vue CLI 遵循一定的加载优先级,确保在不同环境下加载正确的配置。加载顺序如下(后面的文件会覆盖前面的文件):

文件名 描述
.env 默认的配置文件,所有环境都会加载。
.env.[mode] 特定模式的配置文件,例如 .env.development.env.production
.env.local 本地配置文件,用于覆盖默认配置,但不会被提交到版本控制。
.env.[mode].local 特定模式的本地配置文件,例如 .env.development.local.env.production.local

七、案例分析:一个完整的配置示例

假设我们有一个 Vue 项目,需要配置 API 地址和调试模式。我们希望在开发环境中使用测试 API,在生产环境中使用正式 API,并且在开发环境中开启调试模式,在生产环境中关闭调试模式。

我们可以创建以下 .env 文件:

  • .env:
VUE_APP_BASE_URL=/
  • .env.development:
VUE_APP_API_URL=https://test-api.example.com
VUE_APP_DEBUG=true
  • .env.production:
VUE_APP_API_URL=https://api.example.com
VUE_APP_DEBUG=false

然后在你的 Vue 组件中,你可以这样使用这些变量:

<template>
  <div>
    <p>API URL: {{ apiUrl }}</p>
    <p>Debug Mode: {{ debugMode }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      apiUrl: process.env.VUE_APP_API_URL,
      debugMode: process.env.VUE_APP_DEBUG,
    };
  },
};
</script>

当你运行 vue-cli-service serve 时,Vue CLI 会加载 .env.env.development 文件,并将 VUE_APP_API_URL 设置为 "https://test-api.example.com",将 VUE_APP_DEBUG 设置为 "true"

当你运行 vue-cli-service build 时,Vue CLI 会加载 .env.env.production 文件,并将 VUE_APP_API_URL 设置为 "https://api.example.com",将 VUE_APP_DEBUG 设置为 "false"

八、高级技巧:使用函数来动态生成变量

有时候,你可能需要根据一些逻辑来动态生成变量的值。比如,根据当前的日期来生成一个唯一的 ID。

虽然 .env 文件本身不支持函数,但是你可以使用一个小的技巧来实现这个功能:

  1. 创建一个 JavaScript 文件,比如 env.js,在里面编写你的函数:
// env.js
module.exports = {
  generateUniqueId: () => {
    return `id_${Date.now()}`;
  },
};
  1. 在你的 .env 文件中,使用 require() 函数来引入这个 JavaScript 文件,并调用你的函数:
VUE_APP_UNIQUE_ID=require('./env.js').generateUniqueId()

注意:你需要使用 require() 函数,而不是 import 语句。

  1. 在你的 Vue 组件中,你就可以像访问其他变量一样访问这个变量了:
<template>
  <div>
    <p>Unique ID: {{ uniqueId }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      uniqueId: process.env.VUE_APP_UNIQUE_ID,
    };
  },
};
</script>

九、总结

.env 文件和模式是 Vue CLI 中非常重要的概念,它们可以帮助你管理不同环境下的配置,提高代码的可维护性和安全性。通过 dotenv-webpack 插件,Vue CLI 可以将 .env 文件中的变量注入到 process.env 对象中,让你在代码中方便地访问这些变量。

希望今天的讲座对你有所帮助!如果有什么问题,欢迎随时提问。记住,编程就像玩游戏,多动手,多尝试,才能成为高手!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注