在實際項目開發中,前端 er 經常會面對多個環境的接口:開發環境、測試環境、生產環境,因此項目中網絡請求的 baseUrl
也須要跟隨這些環境來變化。前端
可是,咱們通常會使用像 create-react-app
或者 umi
這樣的腳手架來作項目的初始化,這些腳手架將 webpack
的配置黑盒化了,如何在不執行 eject
操做的前提下優雅地配置多個項目環境呢?react
在項目中最好不要一遇到問題就一鍵執行
eject
操做,eject
操做是不可逆的,執行以後會把全部細節都暴露在咱們面前,讓項目目錄變得很龐大。webpack
其實查看 create-react-app 的官方文檔能夠發現,create-react-app
默認是支持多個環境配置文件的:git
.env
:默認。
.env.local
:本地覆蓋。
除 test 以外的全部環境都加載此文件。
.env.development
,
.env.test
,
.env.production
:設置特定環境。
.env.development.local
,
.env.test.local
,
.env.production.local
:設置特定環境的本地覆蓋。
左側的文件比右側的文件具備更高的優先級:github
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
)
例如咱們部門目前開發流程中只有開發環境和測試環境兩種接口(其中,本地開發和測試共用一個環境),web
因此,我須要將測試環境下打包時使用的接口地址指定爲 env.development
中的接口地址,我分別寫了兩份配置文件 .env.development
以及 env.production
,可是根據以上create-react-app
的官方文檔,在執行 build 命令時,默認是加載 .env.production
文件中的變量,因此我在測試服務器上執行 npm run build
命令時就會使得接口地址被指定爲生產環境的接口地址,這顯然不是我想要的。npm
怎麼辦呢?json
官方文檔也給了咱們答案——可使用 dotenv 來作環境變量的管理(dotenv
可將環境變量從 .env 文件加載到 process.env
中。)bash
由於咱們要在命令行中使用,因此咱們須要使用 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
來指定環境"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 文檔 可知,環境變量被放在 config/config.js
下的 define 這個配置中,
若是你使用 TypeScript 開發,那麼配置文件是 config/config.ts。
因此一樣的,咱們能夠將原來的 config/config.ts
作個分身,寫兩份配置文件,分別是 config/config.dev.ts
和 config/config.prod.ts
查看 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",
}
複製代碼
由於各個環境的部署版本之間,配置文件的差別程度可能很大,可是代碼基本是不變的。
固然,上面所說的配置文件不包括內部應用程序的配置(例如,你可能將路由寫成了配置文件)。
判斷一個應用是否正確地將配置排除在代碼以外,一個簡單的方法是看該應用的基準代碼是否能夠馬上開源,而不用擔憂會暴露任何敏感的信息。
——《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 寫項目的時候就是這麼幹的😅,這樣至關於將配置寫在了代碼裏面,不只可維護性比較差,並且別人看你代碼的時候,可讀性也比較差。