Webpack中各種環境變數的正確姿勢

語言: CN / TW / HK

寫在前邊

你還在為Webpack中各種打包配置而煩惱嗎?

今天我們來聊聊webpack中注入環境變數的各種姿勢,或者你會覺得注入環境變數通過命令列注入不就可以了嗎?

如果你有這種想法,耐心看下去我相信你會有不一樣的收穫的~

畢竟所謂成長就是一點一滴積累的過程!讓我們來聊聊Webpack 5中使用環境變數的各種正確姿勢。

文章中從三個方面來講解Webpack流程中的環境變數:

  • 業務程式碼中注入使用webpack環境變數。
  • 官方提供構建過程使用webpack環境變數。
  • 傳統環境變數方法使用webpack構建過程環境變數。

業務程式碼使用環境變數

使用webpack.DefinePlugin外掛在業務程式碼中注入環境變數

相信不少同學已經應用過這種場景,我們需要在打包過程中通過webpack注入一些全域性變數在業務程式碼中使用。

比如我們業務入口檔案有這樣一段程式碼:

js // src/main.js console.log('hello, Environment variable', __WEPBACK__ENV)

我們希望的是業務程式碼中碰到這個__WEBPACK__ENV這個變數時,程式碼中會認識這個變數並且輸出正確字串值pacakges

想一想我們應該怎麼做?

熟悉webpack的同學其實已經想到了,我們可以通過webpack中的definePlugins這個外掛進行注入: js const wepback = require('webpack') // webpack.config.js ... new webpack.DefinePlugin({ __WEBPACK__ENV: JSON.stringify('packages'), TWO: '1+1', });

我們通過在webpackplugins中加入這段配置後當我們執行打包命令後,此時我們業務程式碼中如果出現__WEBPACK__ENV這個變數的話他就會幫我們替換成為'packages'這個字串,從而達到類似環境變數注入的效果。

webpack.DefinePlugin引發的思考

或許你已經很熟悉webpack.definePlugins這個外掛的使用了,彆著急讓我們耐心往下看看,由這段程式碼我們引發出一下的幾個思考:

  1. 'packages'已經是string型別了,為什麼我們還要使用JSON.stringify()進行處理。
  2. 此時的環境變數真的就是所謂的環境變數嗎?

我們先來探討一下這兩個問題:

definePlugin所謂的”環境變數“實現方式

webpack官方文件中這樣講到

(Note that because the plugin does a direct text replacement, the value given to it must include actual quotes inside of the string itself. Typically, this is done either with alternate quotes, such as '"production"', or by using JSON.stringify('production').)

其實他也就是再說webpack.definePlugins本質上是打包過程中的字串替換,比如我們剛才定義的__WEBPACK__ENV:JSON.stringify('packages')

在打包過程中,如果我們程式碼中使用到了__WEPBACK__ENVwebpack會將它的值替換成為對應definePlugins中定義的值,本質上就是匹配字串替換,並不是傳統意義上的環境變數process注入。

這也就回答了我們第二個問題。

JSON.stringify()處理環境變數

接下來我們來看看第一個問題,正所謂實踐出真知。我們來試試對比配置兩次不同的definePlugin來看看結果:

```js // webpack.config.js new webpack.DefinePlugin({ __WEBPACK__ENV: JSON.stringify('packages'), TWO: '1+1', });

// 打包後的程式碼 這裡我們關閉了devtools 羅列出打包後打包後的程式碼 console.log('hello, Environment variable', 'packages') ```

```js // webpack.config.js new webpack.DefinePlugin({ __WEBPACK__ENV: 'packages', TWO: '1+1', });

// 打包後的程式碼 這裡我們關閉了devtools 羅列出打包後打包後的程式碼 console.log('hello, Environment variable', packages) ```

仔細對比這兩段程式碼第一個問題的答案其實已經很明瞭了,針對definePlugin這個外掛我們使用它定義key:value全域性變數時,他會將value進行會直接替換文字。所以我們通常使用JSON.stringify('pacakges')或者"'packages'"

對於DefinePlugin的流程以及需要額外注意的細節我們差不多已經說清除了,但是此時我們定義所謂的全域性變數是在業務程式碼中進行使用的。但此時如果我們對於打包構建過程中想使用環境變數的話需要另一種方式來注入。

構建過程中使用環境變數

通常我們在使用webpack過程中需要根據自己獨特的需求去使用環境變數進行動態打包,比如一些通過動態讀取專案中的資料夾從而在控制檯動態和使用者互動打包對應不同的bundle

此時在構建過程中使用環境變數就顯得非常重要了,所謂構建過程中使用環境變數簡單來說就是在非業務程式碼中,比如webpack.config.js配置檔案中注入環境變數。

我們來看看在專案中輸入這行程式碼:

js npx webpack --env goal=local --env production --progress --config ./webpack.config.js

此時這行程式碼我們相當於執行webpack讀取當前目錄中的webpack.config.js配置檔案進行打包,同時注入兩個環境變數goalprogress他們的值分別為localtrue

這樣我們就可以在配置檔案中使用注入的環境變量了:

```js const path = require('path');

module.exports = (env) => { // Use env. here: console.log('Goal: ', env.goal); // 'local' console.log('Production: ', env.production); // true

return { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), }, }; }; ```

細心的你可以已經發現了,這裡我們的module.exports匯出的是一個包含env的函式,而並不是傳統的物件了。

通常,module.exports 指向配置物件。要使用 env 變數,你必須將 module.exports 轉換成一個函式的方式進行使用。

我們來看看如果不使用函式的話: ```js const path = require('path');

// Use env. here: console.log('Goal: ', process.env.goal); // undefined console.log('Production: ', process.env.production); // undefined

module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), }, }; ```

我們可以看到我們讀取node程序process中的env.goal/production發現兩個分別都是undefined

也就是說通常我們使用--env在命令列中為webpack構建過程注入環境變數時,需要將配置檔案的module.exports匯出一個函式的形式,從而在函式第一個引數中獲取對應的key拿到對應的環境變數value

那麼問題又來了,誒?假如我就是想在nodeprocess中獲得對應的環境變數呢?我應該怎麼辦,我就是不想寫一個函式。

傳統環境變數方法使用webpack構建過程環境變數。

應該怎麼辦呢?其實webpack對應打包的原理就是通過shell命令去執行我們的配置檔案(nodejs配置檔案)。

假如我們在執行命令webpack時注入真正傳統意義上的環境變數而非通過--env是不是就可以了呢?接下來我們來試一試~

這裡我們藉助了一個非常好用的環境變數外掛cross-env,它的使用方式很簡單可以幫助我們在不同環境下linux/windows...中以相同的方式去注入執行時環境變數,接下來我們來使用一下它:

不要忘記安裝npm install --save-dev cross-env

js // package.json "build": "cross-env NAME_W=aaa webpack --config ./webpack.config.js"

我們通過cross-env NAME_W=aaa注入了一個環境變數。

```js // webpack.config.js console.log(process.env.NAME_W, 'env'); // 'aaa'

module.exports = { entry: './src/a.js', }; ```

在我們的嘗試下,發現其實通過命令列傳統方式注入環境變數的形式也是可以的!這樣我們就逃離了匯出必須是一個函式的限制可以"為所欲為了"。

總結

webpack構建以及業務程式碼中,環境變數的注入對於我們的幫助是非常大的。當需要一定體系的前端工程化程式碼時,環境變數無論是在構建過程還是業務程式碼中都起到了至關重要的作用。

看到這裡我們想說到的其實都已經講到了,我們來進行一個簡單的總結吧:

webpack打包業務程式碼時,我們需要使用類似環境變數的方式使用一些全域性變數時,可以通過webpack.DefinePlugin去定義一些變數從而在業務程式碼中使用。(但是需要注意上邊講到的JSON.stringify())。

同時在構建過程中,我們可以通過webpack官方提供的--env引數以及在配置檔案中通過module.exports函式的方式使用--env定義的環境變數。

同時也可以在構建過程中通過我們日常使用的方式注入環境變數而“逃脫”webpack的限制,直接使用命令列中定義的環境變數然後通過process.env.xxx去獲取。