工程项目中.env 文件原理

概述

目前的工程化前端项目中,不可缺少需要配置环境相关信息,根据环境区分,使用什么变量内容等。 .env 文件在我们项目中非常常见,在 vue-clicreate-react-appvite+vue中都有使用。

核心库

dotenv

dotenv 的作用

Dotenv 是一个零依赖模块,可将 .env 文件中的环境变量加载到 process.env 中。

如果需要使用变量,则配合如下扩展包使用dotenv-expand

.env 文件写法:

js 复制代码
NAME=dawei
AGE=18
VITE_APP_ENV=production
VITE_ROUTER_BASE_URL=/ai-form

单从这个文件来看,我们可以知道有如下功能需要实现:

  1. 读取 .env 文件
  2. 解析 .env 文件拆成键值对的对象形式
  3. 赋值到 process.env 上
  4. 最后返回解析后得到的对象

简单实现

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

const parse = function parse(src){
  const obj = {};
  // 用换行符 分割
  // 比如
  /**
     * NAME=若川
     * AGE=18
     * BLOG=https://xx.gitee.io
    */
  src.toString().split('\n').forEach(function(line, index){
    // 用等号分割
    const keyValueArr = line.split('=');
    // NAME
    key = keyValueArr[0];
    // 若川
    val = keyValueArr[1] || '';
    obj[key] = val;
  });
  // { NAME: '若川', ... }
  return obj;
}

const config = function(){
  // 读取 node 执行的当前路径下的 .env 文件
  let dotenvPath = path.resolve(process.cwd(), '.env');
  // 按 utf-8 解析文件,得到对象
  // { NAME: '若川', ... }
  const parsed = parse(fs.readFileSync(dotenvPath, 'utf-8'));

  // 键值对形式赋值到 process.env 变量上,原先存在的不赋值
  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);

// 导出 config parse 函数
module.exports.config = config;
module.exports.parse = parse;

简版的 config 函数还缺失挺多功能,比如:

可由用户自定义路径

可由用户自定义解析编码规则

添加 debug 模式

完善报错输出,用户写的 env 文件自由度比较大,所以需要容错机制。

优化

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

const config = function(options){
    // 读取 node 执行的当前路径下的 .env 文件
    let dotenvPath = path.resolve(process.cwd(), '.env');
    // utf8
    let encoding = 'utf8';
    // debug 模式,输出提示等信息
    let debug = false;
    // 对象
    if (options) {
        if (options.path != null) {
            // 解析路径
            dotenvPath = resolveHome(options.path)
        }
        // 使用配置的编码方式
        if (options.encoding != null) {
            encoding = options.encoding
        }
        // 有配置就设置为 true
        if (options.debug != null) {
            debug = true
        }
    }

    try {
        // 按 utf-8 解析文件,得到对象
        // { NAME: '若川', ... }
        // debug 传递给 parse 函数 便于
        const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug });

        // 键值对形式赋值到 process.env 变量上,原先存在的不赋值
        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 函数主要是一些正则和单双引号、跨平台等细致处理。

一句话总结 dotenv 库的原理。用 fs.readFileSync 读取 .env 文件,并解析文件为键值对形式的对象,将最终结果对象遍历赋值到 process.env 上。

dotenv 把环境变量加载进 process.env 对于前端项目来说还不够,因为浏览器环境是访问不到 process 的,需要通过 webpack 的 DefinePlugin 在构建阶段把变量替换为对应的值

相关推荐
AlienZHOU3 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc6 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师7 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen8 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒8 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow8 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林8188 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang800788 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明8 小时前
第 6 章 组件进阶
前端·vue.js
江华森8 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端