封装dotenv库实现类似Vite加载环境变量的行为
dotenv的作用
将.env
文件中的环境变量加载到 process.env
中
简单使用示例
安装依赖
sh
yarn add dotenv
编写 .env 文件
sh
# 数据库相关内容
DB_USER=test
DB_PWD=123456
DB_PORT=3306
test.js
js
const dotenv = require('dotenv')
const res = dotenv.config()
console.log(res);
console.log(process.env.DB_PORT);
运行结果
注意.env文件需在项目的根目录中(终端执行指令的目录)
Vite中读取.env文件的规则
这里直接贴图原文档内容,不做过多赘述
很详细的叙述了Vite加载额外环境变量的规则
循序渐进的实现
指定存放环境变量文件
- 首先通过
process.cwd()
,获取终端执行命令的路径
js
console.log(process.cwd())
dotenv.config
的参数定义
ts
export interface DotenvConfigOptions {
/**
* You may specify a custom path if your file containing environment variables is located elsewhere.
*/
path?: string;
/**
* You may specify the encoding of your file containing environment variables.
*/
encoding?: string;
/**
* You may turn on logging to help debug why certain keys or values are not being set as you expect.
*/
debug?: boolean;
}
可以看出通过path参数指定环境变量的路径
测试代码如下
js
const dotenv = require('dotenv')
const baseDir = process.cwd()
const res1 = dotenv.config({path:`${baseDir}/.lalala`})
const res2 = dotenv.config({path:`${baseDir}/.lalala2`})
const res3 = dotenv.config({path:`${baseDir}/.不存在`})
console.log(res1);
console.log(res2);
console.log(res3);
console.log(process.env.TEST_ENV);
输出内容分别如下
js
{ parsed: { TEST_ENV: 'test1' } }
{ parsed: { TEST_ENV: 'test2' } }
{
error: 'xxx'
}
test1
可以看出当指定文件存在时,解析的内容会在结果的parsed
属性中
不存在时,解析结果只包含错误信息error
当指定了path参数后,执行多个donEnv.config
方法,只会自动加载第一个的解析结果到process.env中去
加载读取的环境变量
这个步骤简单,直接遍历parsed
属性的内容,将其添加到process.env属性上即可
js
function load(parseEnvObj) {
const { parsed } = parseEnvObj
if (parsed && parsed instanceof Object) {
Object.getOwnPropertyNames(parsed).forEach((k) => {
process.env[k] = parsed[k]
})
}
}
测试
js
const baseDir = process.cwd()
const res1 = dotenv.config({ path: `${baseDir}/.lalala` })
const res2 = dotenv.config({ path: `${baseDir}/.lalala2` })
const res3 = dotenv.config({ path: `${baseDir}/.不存在` })
load(res1);
load(res2);
load(res3);
console.log(process.env.TEST_ENV); // test2
实现Vite的读取规则
js
function loadEnv() {
const baseDir = process.cwd()
// .env
load(dotenv.config({ path: `${baseDir}/.env` }))
// .env.local
load(dotenv.config({ path: `${baseDir}/.env.local` }))
// .env.[mode]
load(dotenv.config({ path: `${baseDir}/.env.${process.env.NODE_ENV}` }))
// .env.[mode].local
load(dotenv.config({ path: `${baseDir}/.env.${process.env.NODE_ENV}.local` }))
}
简单封装成一个函数,直接调用loadEnv
mode
的设置可以通过cross-env库设置,如下所示
package.json
json
{
"scripts": {
"dev": "cross-env NODE_ENV=development node ./tests/test.js",
},
}
限制环境变量前缀
简单修改后的代码,添加了前缀的过滤
js
function load(parseEnvObj, prefix = '') {
const { parsed } = parseEnvObj
if (parsed && parsed instanceof Object) {
Object.getOwnPropertyNames(parsed).forEach((k) => {
if (k.indexOf(prefix) === 0) {
process.env[k] = parsed[k]
}else{
process.env[k] = undefined
}
})
}
}
function loadEnv(options = {}) {
const { prefix = '' } = options
const baseDir = process.cwd()
// .env
load(dotenv.config({ path: `${baseDir}/.env` }), prefix)
// .env.local
load(dotenv.config({ path: `${baseDir}/.env.local` }), prefix)
// .env.[mode]
load(dotenv.config({ path: `${baseDir}/.env.${process.env.NODE_ENV}` }), prefix)
// .env.[mode].local
load(dotenv.config({ path: `${baseDir}/.env.${process.env.NODE_ENV}.local` }), prefix)
}
测试
.env
内容
sh
# 数据库相关内容
DB_USER=test
DB_PWD=123456
DB_PORT=3306
SUGAR_USER=sugar
js
loadEnv()
console.log(process.env.DB_USER); // test
console.log(process.env.SUGAR_USER); // sugar
js
loadEnv({prefix:'SUGAR'})
console.log(process.env.DB_USER); // undefined
console.log(process.env.SUGAR_USER); // sugar
最终版
js
// 读取配置的环境变量
const dotenv = require('dotenv')
function load(parseEnvObj, prefix = '') {
const { parsed } = parseEnvObj
if (parsed && parsed instanceof Object) {
Object.getOwnPropertyNames(parsed).forEach((k) => {
if (k.indexOf(prefix) === 0) {
process.env[k] = parsed[k]
}else{
process.env[k] = undefined
}
})
}
}
function loadEnv(options = {}) {
const { prefix = '' } = options
const baseDir = process.cwd()
// .env
load(dotenv.config({ path: `${baseDir}/.env` }), prefix)
// .env.local
load(dotenv.config({ path: `${baseDir}/.env.local` }), prefix)
// .env.[mode]
load(dotenv.config({ path: `${baseDir}/.env.${process.env.NODE_ENV}` }), prefix)
// .env.[mode].local
load(dotenv.config({ path: `${baseDir}/.env.${process.env.NODE_ENV}.local` }), prefix)
}
module.exports = loadEnv
使用
js
const loadEnv = require('modulePath')
loadEnv()
// or
loadEnv({prefix:'xx'})
TODO
下一次分享一下Vite中这部分的源码实现逻辑