前端项目中 .env 文件的原理和实现

为什么要用 .env?

在前端开发中,我们经常需要区分不同环境下的配置,比如:

  • 开发环境(开发调试用的 API 地址、本地调试开关);
  • 测试环境(给测试同学用的接口域名、测试账号);
  • 生产环境(线上稳定接口、CDN 域名、是否启用 mock 数据等)。

如果把这些配置直接写死在代码里,不仅管理麻烦,还容易出现「改配置 → 重新打包 → 发错环境」的坑。

这时候 .env 文件就派上用场了。

👉 它能把不同环境的变量抽离出来,统一放在一个文件里,通过工具在打包或运行时注入到代码中,大大提升了项目的可维护性。


环境准备

我们先来简单准备下环境。

如果想直接看 dotenv 的源码,可以克隆官方项目:

bash 复制代码
git clone https://github.com/motdotla/dotenv.git
# cd dotenv && yarn i
# VSCode 打开当前项目
code .

dotenv 的作用

官方项目地址:

👉 github.com/motdotla/do...

dotenv 是一个零依赖模块,用来把 .env 文件中的环境变量加载到 process.env 中。

如果需要进一步使用变量展开,还可以配合 dotenv-expand 一起使用。

在前端项目里,.env 文件非常常见,比如 vue-clicreate-react-app 都支持它。


.env 文件写法示例

ini 复制代码
NAME=gengyun
AGE=18
VITE_APP_ENV=production
VITE_ROUTER_BASE_URL=/ai-form
VITE_APP_PRO_USE_MOCK=true

从上面这个 .env 文件就能看出来,.env 的主要用途就是:

  1. 读取 .env 文件;
  2. 解析成 key=value 形式的对象;
  3. 挂载到 process.env 上;
  4. 最后返回解析好的对象,方便使用。

一个简易版实现

下面写一个简单的实现,核心就是 用 Node.js 读取文件,解析成对象,然后赋值给 process.env

javascript 复制代码
const fs = require('fs');
const path = require('path');

const parse = function (src) {
  const obj = {};
  src.toString().split('\n').forEach(function (line) {
    const keyValueArr = line.split('=');
    const key = keyValueArr[0];
    const val = keyValueArr[1] || '';
    obj[key] = val;
  });
  return obj;
}

const config = function () {
  const dotenvPath = path.resolve(process.cwd(), '.env');
  const parsed = parse(fs.readFileSync(dotenvPath, 'utf-8'));

  Object.keys(parsed).forEach(function (key) {
    if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
      process.env[key] = parsed[key];
    }
  });

  return parsed;
};

console.log(config());
console.log(process.env);

module.exports.config = config;
module.exports.parse = parse;

虽然能用,但这个 config 函数还很简陋,缺少一些常见功能,比如:

  • 支持用户自定义 .env 文件路径;
  • 支持自定义编码;
  • 添加 debug 模式,方便输出提示;
  • 更友好的报错信息(毕竟 .env 文件写法很随意)。

升级版实现

官方的 dotenv 库在源码里对这些功能做了处理,比如路径解析、编码设置、调试日志等。我们也可以稍微扩展一下:

ini 复制代码
const fs = require('fs');
const os = require('os');
const path = require('path');

function resolveHome(envPath) {
  return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath;
}

const config = function (options) {
  let dotenvPath = path.resolve(process.cwd(), '.env');
  let encoding = 'utf8';
  let debug = false;

  if (options) {
    if (options.path != null) {
      dotenvPath = resolveHome(options.path);
    }
    if (options.encoding != null) {
      encoding = options.encoding;
    }
    if (options.debug != null) {
      debug = true;
    }
  }

  try {
    const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug });

    Object.keys(parsed).forEach(function (key) {
      if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
        process.env[key] = parsed[key];
      } else if (debug) {
        console.log(`"${key}" is already defined in process.env and will not be overwritten`);
      }
    });

    return parsed;
  } catch (e) {
    return { error: e };
  }
};

dotenv 官方的 parse 函数还处理了更多细节,比如:

  • 正则匹配;
  • 支持单双引号;
  • 跨平台兼容(Windows/Linux 换行符不同)。

一句话总结

dotenv 的原理很简单:

👉 用 fs.readFileSync 读取 .env 文件,解析成 key=value 对象,然后把对象里的变量挂到 process.env 上。


前端项目里的额外说明

不过要注意:

Node.js 环境 (如服务端)下,直接挂到 process.env 就能用了。

但在 前端项目 (浏览器环境)里,浏览器本身是访问不到 process 的。

所以像 vue-clivite 这样的工具,会在构建阶段借助 webpack 的 DefinePlugin (或 Vite 内置的变量替换机制)把 .env 文件里的变量直接替换成具体值,注入到代码里。


总结 & 扩展思考

.env 文件的核心价值在于:

  • 配置解耦:不用把不同环境的配置写死在代码里;
  • 方便管理:一套代码,多套环境;
  • 安全性:敏感信息可以集中管理,避免硬编码到仓库。

扩展思考:

  • 前端项目 中,哪些变量适合放在 .env?哪些不能?(比如 API 地址 可以,秘钥 一定不要!)
  • 多人协作 时,如何管理不同成员的本地 .env 文件?(比如 .env.local 不要提交到 git)
  • 除了 dotenv,有没有类似的工具?

这些问题都值得在实际项目中思考和总结。

相关推荐
东风破_1 分钟前
一文搞懂 JavaScript 变量声明:var、let、const 到底有什么区别?
前端·javascript
问心无愧05134 分钟前
ctf show web入门261
android·前端·笔记
触底反弹6 分钟前
你真的理解 JavaScript 变量提升(Hoisting)吗?从 V8 引擎编译原理深入剖析
前端·面试
蜡台19 分钟前
Vue2 使用 typescript 教程
前端·vue.js·typescript
光影少年31 分钟前
Redux Toolkit 用法、解决原生Redux 冗余问题
开发语言·前端·javascript·react.js·中间件·前端框架·ecmascript
云水一下38 分钟前
JavaScript 从零基础到精通系列:DOM 操作与事件驱动编程
前端·javascript
努力发光的程序员1 小时前
面试官与程序员谢飞机的3轮Java大厂面试问答实录:涵盖Spring Boot、微服务与数据库技术
java·jvm·spring boot·redis·面试·hibernate·microservices
ZC跨境爬虫1 小时前
跟着 MDN 学CSS day_32:(Web字体深度解析与实践指南)
前端·javascript·css·ui·html
Rick19931 小时前
Redis 高频面试 10 题
数据库·redis·面试