在實際專案開發中,前端 er 常常會面對多個環境的介面:開發環境、測試環境、生產環境,所以專案中網路請求的 baseUrl
也需要跟隨這些環境來變化。
但是,我們一般會使用像 create-react-app
或者 umi
這樣的腳手架來做專案的初始化,這些腳手架將 webpack
的配置黑盒化了,如何在不執行 eject
操作的前提下優雅地配置多個專案環境呢?
在專案中最好不要一遇到問題就一鍵執行
eject
操作,eject
操作是不可逆的,執行之後會把所有細節都暴露在我們面前,讓專案目錄變得很龐大。
create-react-app 配置多環境介面
其實檢視 create-react-app 的官方文件可以發現,create-react-app
預設是支援多個環境配置檔案的:
.env
:預設。.env.local
:本地覆蓋。除 test 之外的所有環境都載入此檔案。.env.development
,.env.test
,.env.production
:設定特定環境。.env.development.local
,.env.test.local
,.env.production.local
:設定特定環境的本地覆蓋。
左側的檔案比右側的檔案具有更高的優先順序:
npm start
:.env.development.local
,.env.development
,.env.local
,.env
npm run build
:.env.production.local
,.env.production
,.env.local
,.env
npm test
:.env.test.local
,.env.test
,.env
(注意沒有.env.local
)
例如我們部門目前開發流程中只有開發環境和測試環境兩種介面(其中,本地開發和測試共用一個環境),
所以,我需要將測試環境下打包時使用的介面地址指定為 env.development
中的介面地址,我分別寫了兩份配置檔案 .env.development
以及 env.production
,但是根據以上create-react-app
的官方文件,在執行 build 命令時,預設是載入 .env.production
檔案中的變數,所以我在測試伺服器上執行 npm run build
命令時就會使得介面地址被指定為生產環境的介面地址,這顯然不是我想要的。
怎麼辦呢?
官方文件也給了我們答案——可以使用 dotenv 來做環境變數的管理(dotenv
可將環境變數從 .env 檔案載入到 process.env
中。)
因為我們要在命令列中使用,所以我們需要使用 dotenv-cli。
話不多說,讓我們開始吧~
寫好各個環境的配置檔案
首先,我們可以寫好每個環境下的配置檔案。
# .env.development
REACT_APP_BASE_URL='http://development.xxx.xxx'
複製程式碼
# env.production
REACT_APP_BASE_URL='http://production.xxx.xxx'
複製程式碼
修改 package.json
中的 scripts
來指定環境
先來安裝一下 dotenv-cli
,作為專案的開發依賴:
yarn add --dev dotenv-cli
# 或者
npm i -D dotenv-cli
複製程式碼
然後修改 package.json
中的 scripts
來指定環境:
"scripts": {
"start": "react-app-rewired start",
"build:dev": "dotenv -e .env.development react-app-rewired build",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
}
複製程式碼
這樣,當我需要在測試伺服器上打包前端程式碼時,我就可以執行npm run build:dev
來指定使用 .env.development
中的環境變數了~
umi 配置多環境介面
有了以上的經驗我們就可以知道,其實多環境配置,不外乎就是將各個環境的配置檔案分開,並使用額外的手段來在打包時指定對應環境的配置檔案。
寫好各個環境的配置檔案
檢視 UMI 文件 可知,環境變數被放在 config/config.js
下的 define 這個配置中,
如果你使用 TypeScript 開發,那麼配置檔案是 config/config.ts。
所以同樣的,我們可以將原來的 config/config.ts
做個分身,寫兩份配置檔案,分別是 config/config.dev.ts
和 config/config.prod.ts
修改 package.json 中的 scripts 來指定環境
檢視 umi
生成的模版專案中的package.json
可以發現: umi
預設是使用 cross-env來為 umi
打包指定配置檔案。所以我們將package.json
中的 scripts
改寫如下:
"scripts": {
"start": "react-app-rewired start",
"build-dev": "cross-env UMI_ENV=dev umi dev",
"build-test": "cross-env UMI_ENV=test umi build",
"build-prod": "cross-env UMI_ENV=prod umi build",
}
複製程式碼
Tips:將配置和程式碼分開儲存
因為各個環境的部署版本之間,配置檔案的差異程度可能很大,但是程式碼基本是不變的。
當然,上面所說的配置檔案不包括內部應用程式的配置(例如,你可能將路由寫成了配置檔案)。
判斷一個應用是否正確地將配置排除在程式碼之外,一個簡單的方法是看該應用的基準程式碼是否可以立刻開源,而不用擔心會暴露任何敏感的資訊。
——《The Twelve-Factor App》
反面教材
一個比較典型的反面教材就是在程式碼中再寫一份類似 getBaseUrl.js
這樣的檔案來做環境判斷:
// getBaseUrl.js
const TEST_DOMAIN = process.env.REACT_APP_BASE_URL
const PRODUCTION_DOMAIN = process.env.REACT_APP_PRODUCTION_BASE_URL
let domain = TEST_DOMAIN
switch (process.env.NODE_ENV) {
case 'development':
domain = TEST_DOMAIN
break
case 'production':
domain = PRODUCTION_DOMAIN
break
default:
domain = TEST_DOMAIN
break
}
export default domain
複製程式碼
上面的變數是 .env
檔案裡面寫好的變數:
# .env
REACT_APP_DEVELOPMENT_BASE_URL='http://xxxxxx' # 開發環境/測試環境的介面地址
REACT_APP_PRODUCTION_BASE_URL='http://xxxxxx' # 生產環境的介面地址
複製程式碼
其實我自己在剛開始用 React 寫專案的時候就是這麼幹的?,這樣相當於將配置寫在了程式碼裡面,不僅可維護性比較差,而且別人看你程式碼的時候,可讀性也比較差。