theme: cyanosis
highlight: a11y-dark
前言
日常开发中,相信不少项目都会有.env或者.env.development这样的文件,那么这些文件的作用是什么呢?今天就一起来学习探讨一下~
收获清单
- [x] env文件的作用
- [x] dotenv 原理和实现
- [x] 如何使用fs模块获取文件并解析
env文件的作用
提到env不难会联想到环境变量,为什么要设置环境变量呢?简言之,当你想要同一套代码应用到不同的环境并在某些功能上有所区分时,区分这些不同的环境就需要用到环境变量。如下图 vue-cli 中的环境变量。在env文件设置完变量后通过项目代码中通过process.env
来访问变量,那么是什么把env文件加载到process.env
中的呢?就是今天的主角dotenv了!
源码下载
git clone https://github.com/motdotla/dotenv
cd dotenv
dotnev简介
使用教程可以看README,官方简介是Dotenv是一个零依赖模块,它将.env文件中的环境变量加载到process.env中。将配置与代码分开存储在环境中是基于十二因素应用程序方法的。
开启调试
调试截图
源码解析
引入模块
// 文件模块
const fs = require('fs')
// 路径模块
const path = require('path')
// os模块,提供基本的系统操作函数
const os = require('os')
config函数
function config (options) {
//获取env文件当前Node.js进程执行时的文件夹地址——工作目录
let dotenvPath = path.resolve(process.cwd(), '.env')
let encoding = 'utf8'
const debug = Boolean(options && options.debug)
const override = Boolean(options && options.override)
// 获取options配置:路径、编码方式等
if (options) {
if (options.path != null) {
dotenvPath = _resolveHome(options.path)
}
if (options.encoding != null) {
encoding = options.encoding
}
}
try {
// Specifying an encoding returns a string instead of a buffer
// 用fs.readFileSync(dotenvPath, { encoding })读取文件然后进行解析
const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }))
// 把解析出的env文件值遍历赋值到process.env
Object.keys(parsed).forEach(function (key) {
if (!Object.prototype.hasOwnProperty.call(process.env, key)) {
process.env[key] = parsed[key]
} else {
if (override === true) {
process.env[key] = parsed[key]
}
if (debug) {
if (override === true) {
_log(`"${key}" is already defined in \`process.env\` and WAS overwritten`)
} else {
_log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`)
}
}
}
})
return { parsed }
} catch (e) {
if (debug) {
_log(`Failed to load ${dotenvPath} ${e.message}`)
}
return { error: e }
}
}
_resolveHome函数
// os.homedir()方法是os模块的内置应用程序编程接口,用于获取当前用户的主目录路径。
function _resolveHome (envPath) {
return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath
}
parse 函数
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
// Parser src into an Object
function parse (src) {
const obj = {}
// Convert buffer to string
let lines = src.toString()
// Convert line breaks to same format
// 换行符替换
lines = lines.replace(/\r\n?/mg, '\n')
let match
// 检索解析内容是否匹配正则形如**=**,exec返回匹配则返回函数值
while ((match = LINE.exec(lines)) != null) {
// 对应键
const key = match[1]
// Default undefined or null to empty string
// 对应值,.trim()删除两端空格
let value = (match[2] || '')
// Remove whitespace
value = value.trim()
// Check if double quoted
const maybeQuote = value[0]
// Remove surrounding quotes
value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2')
// Expand newlines if double quoted
if (maybeQuote === '"') {
value = value.replace(/\\n/g, '\n')
value = value.replace(/\\r/g, '\r')
}
// Add to object
// 返回由键值对组成的对象
obj[key] = value
}
return obj
}
总结
今天调试分析了dotenv的源码,总结一下原理就是:先利用fs.readFileSync读取env文件内容,然后借助正则解析返回键值对组成的对象赋值到process.env
。本期源码主要函数仅108行,除了匹配正则有点晦涩难懂外整体理解起来不难,况且涉及了node.js的文件读取、正则应用、Object.keys遍历对象key值等实用知识,同时又是工作中常见又容易被忽略的,值得一学~
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容