從源碼分析vue-cli@3.0環境變量配置

前言

在開始以前,咱們先來看下官方文檔說明;查看vue-cli文檔中有這麼一句話:html

只有以 VUE_APP_ 開頭的變量會被 webpack.DefinePlugin 靜態嵌入到客戶端側的包中。
複製代碼

由此咱們能夠知道,vue-cli 是有針對 VUE_APP_ 這個變量進行配置。接下來讓咱們查看源碼,看下vue-cli是怎麼實現的。vue

源碼分析

查看根路徑的 package.json 文件咱們能夠知道,當執行 npm run serve 時會執行 vue-cli-service;因此咱們找到該文件的執行地址node_modules/@vue/cli-servicenode

分析其 package.json 咱們知道,當咱們執行 vue-cli-service 時候會調用文件 lib/Service.jsinit 函數:webpack

init (mode = process.env.VUE_CLI_MODE) {
    ...
    this.mode = mode

    // load mode .env
    if (mode) {
      this.loadEnv(mode)
    }
    // load base .env
    this.loadEnv()
    ...
  }
複製代碼

緊接着咱們再來看loadEnv方法git

loadEnv (mode) {
    const logger = debug('vue:env')
    const basePath = path.resolve(this.context, `.env${mode ? `.${mode}` : ``}`)
    // 注意這裏,無論你有沒有定義它都會在後面加上一個.local的後綴,感受這裏不是很好
    const localPath = `${basePath}.local`

    const load = path => {
        try {
            const env = dotenv.config({ path, debug: process.env.DEBUG })
            dotenvExpand(env)
            logger(path, env)
        } catch (err) {
            // only ignore error if file is not found
            if (err.toString().indexOf('ENOENT') < 0) {
                error(err)
            }
        }
    }

    load(localPath)
    load(basePath)

    ...
}
複製代碼

這裏會先調用 dotenv(位於 node_modules/dotenv )的 config 函數,最終會返回這樣的格式 { parsed: { YOUR_ENV_KEY: '你設定的環境變量值' } }。 而且咱們能夠看到,這裏已經將值存放到process.env中了。github

function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ {
  ...

  try {
    // specifying an encoding returns a string instead of a buffer
    const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug })

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

    return { parsed }
  } catch (e) {
    return { error: e }
  }
}
複製代碼

這裏再說下 parse 方法,會讀取你設置的全部 .env 文件,而後將裏面的數據轉換成對象,簡單來講就是 以換行符號來循環,用正則匹配出內容,最終造成以{key: value}的格式輸出。web

const NEWLINE = '\n'
const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/
const RE_NEWLINES = /\\n/g

function parse (src /*: string | Buffer */, options /*: ?DotenvParseOptions */) /*: DotenvParseOutput */ {
  const debug = Boolean(options && options.debug)
  const obj = {}

  // convert Buffers before splitting into lines and processing
  src.toString().split(NEWLINE).forEach(function (line, idx) {
    // matching "KEY' and 'VAL' in 'KEY=VAL'
    const keyValueArr = line.match(RE_INI_KEY_VAL)
    // matched?
    if (keyValueArr != null) {
      const key = keyValueArr[1]
      // default undefined or missing values to empty string
      let val = (keyValueArr[2] || '')
      const end = val.length - 1
      const isDoubleQuoted = val[0] === '"' && val[end] === '"'
      const isSingleQuoted = val[0] === "'" && val[end] === "'"

      // if single or double quoted, remove quotes
      if (isSingleQuoted || isDoubleQuoted) {
        val = val.substring(1, end)

        // if double quoted, expand newlines
        if (isDoubleQuoted) {
          val = val.replace(RE_NEWLINES, NEWLINE)
        }
      } else {
        // remove surrounding whitespace
        val = val.trim()
      }

      obj[key] = val
    } else if (debug) {
      log(`did not match key and value when parsing line ${idx + 1}: ${line}`)
    }
  })

  return obj
}
複製代碼

而後咱們再來看下 dotenvExpand 方法,找到它位於 node_modules/dotenv-expand 下,這裏就沒啥好說的,就是將幾個環境變量的值放到一塊兒vue-cli

接下來讓咱們回到開頭說的 只有以 VUE_APP_ 開頭的變量會被 webpack.DefinePlugin 靜態嵌入到客戶端側的包中,這個是在那裏配置的呢?npm

咱們知道 vue-cli 會對 webpack 進行配置擴展,因此咱們發如今 cli-service/lib/config/app.jsjson

const resolveClientEnv = require('../util/resolveClientEnv')
    webpackConfig
      .plugin('define')
        .use(require('webpack/lib/DefinePlugin'), [
          resolveClientEnv(options)
        ])
複製代碼

咱們接着找到 resolveClientEnv 方法,能夠看到是在這裏定義的,而且會將非 VUE_APP_ 開頭的過濾掉

const prefixRE = /^VUE_APP_/

module.exports = function resolveClientEnv (options, raw) {
  const env = {}
  Object.keys(process.env).forEach(key => {
    if (prefixRE.test(key) || key === 'NODE_ENV') {
      env[key] = process.env[key]
    }
  })
  // 須要注意的是 `baseUrl` 從 `Vue CLI 3.3` 起已棄用,請使用 `publicPath` 。如下源碼涉及此改動
  env.BASE_URL = options.publicPath

  if (raw) {
    return env
  }

  for (const key in env) {
    env[key] = JSON.stringify(env[key])
  }
  return {
    'process.env': env
  }
}
複製代碼

總結

  1. .env.env.local 定義的環境變量會被全局引用,並會與其它環境變量合併
  2. 聲明環境變量必須以 VUE_APP_ 開頭,否則會被過濾掉
  3. 除了 VUE_APP_* 變量以外,在你的應用代碼中始終可用的還有兩個特殊的變量:NODE_ENVBASE_URL

最後,謝謝你們的觀看,若是想觀看個人更多文章,請點擊前往。 若是對您有幫助,請爲我點個小星星。

也歡迎你們交流分享本身的開發心得~

本文做者: Echi
本文連接: juejin.im/user/585e36…
版權聲明: 本文章除特別聲明外,均採用 @BY-NC-SA 許可協議。轉載請註明出處!

相關文章
相關標籤/搜索