首頁 > 軟體

專案中常用的 .env 檔案原理原始碼解析

2022-12-27 14:01:37

前言

dotenv 是一個用於載入環境變數的庫,在 Node.js 應用程式中可以使用它來簡化對環境變數的存取。在日常開發中起到了很重要的作用。

學習目標:

  • 學會 dotenv 原理和實現
  • 學會使用 fs模組 獲取檔案並解析

資源:

原始碼地址:dotenv

如何使用

使用 dotenv 庫,可以在應用程式中建立一個名為 .env 的檔案,並在該檔案中儲存環境變數。然後,可以使用 dotenv 庫將這些變數載入到 Node.js 應用程式中。

例如,您可以在 .env 檔案中儲存以下內容:

DB_HOST=localhost
DB_USERNAME=user
DB_PASSWORD=password

使用以下程式碼將這些變數載入到應用程式中:

require('dotenv').config();
const dbHost = process.env.DB_HOST;
const dbUsername = process.env.DB_USERNAME;
const dbPassword = process.env.DB_PASSWORD;

原始碼解析

閱讀原始碼之前,可以猜測 dotenv 所做的工作有如下幾點。

  • 讀取 .env 檔案
  • 解析檔案
  • 將解析出的變數賦值給 process.env

來看下原始碼是如何完成上述功能的。

讀取檔案

function config (options) {
  let dotenvPath = path.resolve(process.cwd(), '.env')
  let encoding = 'utf8'
  const debug = Boolean(options && options.debug)
  const override = Boolean(options && options.override)
  if (options) {
    if (options.path != null) {
      dotenvPath = _resolveHome(options.path)
    }
    if (options.encoding != null) {
      encoding = options.encoding
    }
  }
}

程式碼中定義了一個變數 dotenvPath,並將其賦值為使用 path.resolve 函數處理後的路徑。

path.resolve 函數會從右到左依次遍歷引數,並返回一個絕對路徑。函數的第一個引數是 process.cwd,它返回 Node.js 程序的當前工作目錄。第二個引數是字串 '.env',它表示要在當前工作目錄中查詢的檔名。

之後會進行一些引數的判斷,如果引數中有path這個變數,則使用_resolveHome函數處理:

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

os.homedir 函數返回當前使用者的主目錄路徑。

_resolveHome 函數可用於將以波浪號開頭的路徑解析為主目錄的實際路徑。例如,如果 envPath 等於 '~/documents/file.txt',則函數將返回 '/home/user/documents/file.txt'(在基於 Unix 的系統上)或 'C:Usersuserdocumentsfile.txt'(在 Windows 上)。

解析檔案

// 使用 `fs.readFileSync` 函數以指定的編碼方式從檔案系統中讀取檔案內容
const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }))
// 解析檔案
function parse (src) {
  const obj = {}
  // 轉為string型別
  let lines = src.toString()
  // 將換行符轉換為相同的格式
  lines = lines.replace(/rn?/mg, 'n')
  let match
  while ((match = LINE.exec(lines)) != null) {
    const key = match[1]
    // Default undefined or null to empty string
    let value = (match[2] || '')
    // Remove whitespace
    value = value.trim()
    // Check if double quoted
    const maybeQuote = value[0]
    // Remove surrounding quotes
    value = value.replace(/^(['"`])([sS]*)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
}

首先使用正規表示式 LINE 來匹配字串 lines 中的內容。

const LINE = /(?:^|^)s*(?:exports+)?([w.-]+)(?:s*=s*?|:s+?)(s*'(?:\'|[^'])*'|s*"(?:\"|[^"])*"|s*`(?:\`|[^`])*`|[^#rn]+)?s*(?:#.*)?(?:$|$)/mg

這個正規表示式的目的是匹配類似於環境變數的行。它可以匹配以下格式的行:

VARNAME=value
VARNAME: value
export VARNAME=value
export VARNAME: value

最後會返回一個包含所有變數的物件。

賦值操作

  try {
    // Specifying an encoding returns a string instead of a buffer
    const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }))
    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 }
  } 

拿到解析後的物件,使用 Object.keys(parsed) 獲取所有的鍵,然後使用forEach迴圈將所有的鍵新增到process.env 中。

總結

dotenv 的功能用一句話來概括就是:解析env檔案將其變數新增到process.env中,其中解析部分主要是使用了正規表示式來匹配4種格式的鍵值對。

以上就是專案中常用的 .env 檔案原理原始碼解析的詳細內容,更多關於.env 檔案原理的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com