原來 React 專案多環境打包是如此的簡單

去衝浪鴨發表於2019-12-07

在實際專案開發中,前端 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.tsconfig/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 寫專案的時候就是這麼幹的?,這樣相當於將配置寫在了程式碼裡面,不僅可維護性比較差,而且別人看你程式碼的時候,可讀性也比較差。

參考檔案

相關文章