有些時候我們會需要針對不同的環境設置不同的key、API…等,或者不希望把隱私的資料直接明晃晃寫出來,就可以將這些隱私資料存到環境變數(Enviroment Variables)中。
要在 RN 管理環境變數可以使用 react-native-dotenv 這個庫。
安裝 react-native-dotenv
npm install -D react-native-dotenv @types/react-native-dotenv
// or
yarn add -D react-native-dotenv @types/react-native-dotenv
接著在 babel.config.js
的 plugins 加上 ['module:react-native-dotenv']
:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
['module:react-native-dotenv'],
//...
]
}
這邊使用默認設置就好,如果需要進階設置可以參考官方文檔
基本使用
在專案根目錄新增 .env
:
API_URL=https://api.example.org
API_TOKEN=abc123
在需要使用環境變數的檔案中 import from '@env'
:
import { API_URL, API_TOKEN } from '@env'
fetch(`${API_URL}/users`, {
headers: {
'Authorization': `Bearer ${API_TOKEN}`
}
})
支持 TypeScript
現在 import 環境變數時 IDE 應該會提示錯誤:
這是因為我們需要為 module 進行型別宣告。
在專案根目錄中建立一個 _types_
資料夾,並且新增 env.d.ts
declare module '@env' {
export const API_URL: string;
}
在裡面定義環境變數的型別後,這個錯誤提示就會消失,並且 IDE 會提示你有哪些變數可使用:
如果還是一樣提示錯誤,那就需要在 tsconfig.json
中指定 typeRoots
:
{
"extends": "@tsconfig/react-native/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"typeRoots": ["./src/_types_"]
}
}
題外話,如果希望描述環境變數的用處,可以使用 JSDoc:
declare module '@env' {
/**
* API URL
*/
export const API_URL: string;
}
多個環境
如果有多個環境需要設置不同的環境變數,比如說 dev
, prod
,就可以新建三個 .env:
- .env
API_URL=http://192.168.0.xxx
- .env.dev
API_URL=https://dev.api.xxxxxxx
- .env.prod
API_URL=https://api.xxxxxxx
記得要將
.env
,.env.dev
,.env.prod
加到 .gitignore 中。
接著在 package.json
中新增不同的啟動 script:
- 本地使用 npm run start,默認讀取
.env
- dev環境使用
npm run start:dev
- prod環境使用
npm run start:prod
{
"scripts": {
"start": "npx react-native start",
"start:dev": "NODE_ENV=dev npx react-native start",
"start:prod": "NODE_ENV=prod npx react-native start",
}
}
Expo 管理環境變數
SDK49(含)以上
- 使用
EXPO_PUBLIC_
作為環境變數名的前綴 - 不需要安裝任何第三方庫就可以直接獲取環境變數的值
// .env
EXPO_PUBLIC_API_URL=https://staging.example.com
EXPO_PUBLIC_API_KEY=abc123
import { Button } from 'react-native'
function Post() {
const apiUrl = process.env.EXPO_PUBLIC_API_URL
async function onPress() {
await fetch(apiUrl, { ... })
}
return <Button onPress={onPress} title="Post" />
}
多個環境的設置方法都一樣,只不過是把 react-native start
改為 expo start
而已:
{
"scripts": {
"start": "npx expo start",
"start:dev": "NODE_ENV=dev npx expo start",
"start:prod": "NODE_ENV=prod npx expo start"
}
}
SDK49 以下
有兩種方式(其實不只兩種,不過這邊就簡單官方建議的兩種):
一、用 eas secret
管理,詳細的請參考官方文檔
二、使用 dotenv 和 expo-constants
npx expo install dotenv expo-constants
一樣是將環境變數存在 .env
// .env
API_URL=https://api.production.com
PROJECT_ID=xxxxxxxxxxxxxxxxxxxxx
然後在 app.json
或者 app.config.ts
的 extra 中定義環境變數:
<em>// app.config.ts</em>
import 'dotenv/config'
module.exports = ({ config }) => {
return {
...config,
extra: {
API_URL: process.env.API_URL,
eas: {
projectId: process.env.PROJECT_ID,
},
},
}
}
在 App 中使用:
import Constants from 'expo-constants'
const API_URL = Constants.manifest.extra.API_URL;
console.log(API_URL) <em>// https://api.production.com</em>
eas build 時獲取環境變數
如果使用 eas build 指令,需要先將 .gitignore
中的 .env
註釋後再 build,否則 expo 無法吃到環境變數的值。
2024-03-08 補充:可以使用
.easignore
在 eas build 時取代.gitignore
。
注意:如果在eas.json
中設定requireCommit: true
,則不支援.easignore
參考:How projects are uploaded to EAS Build
確認 eas build 時有沒有吃到環境變數
eas build 時有一個階段叫做 Read app config
點開來確定 extra 是否為空就可以知道到底有沒有吃到環境變數。
像這張圖這樣就是沒吃到:
這樣就是吃到了:
在 CI/CD 流程中管理環境變數
可以在 CI/CD 時將環境變數寫入應用的 .env 中,這邊以 github 和 gitlab 為例。
Github
將環境變數新增至倉庫的 actions secrets
然後在 workflow yml 中添加以下 step:
echo "APP_ENV_KEY=${SECRET_KEY}" >> .env
即將環境變數的值寫入.env
中sed -i "s@.env@@g" .gitignore
代表在 .gitignore 中註釋 .env,如果不在 .gitignore 中註釋掉 .env 的話 eas build 時是無法吃到環境變數的。
env:
BASE_URL: ${{ secrets.API_URL }}
steps:
- name: Set Variables
run: |
echo "BASE_URL=${BASE_URL}" >> .env
sed -i "s@.env@@g" .gitignore
# .env
BASE_URL=
Gitlab
在 repo 的 Settings – CI/CD – Variables 新增環境變數:
注意:設置為
protected
的變數只有protected
的分支可以獲取得到。
在 .gitlab-ci.yml
中使用 echo "APP_ENV_KEY=${REPO_VARIABLE_NAME}" >> .env
就可以將環境變數的值寫入 .env 中:
# .gitlab-ci.yml
init:develop:
stage: init
script:
- echo "BASE_URL=${API_URL}" >> .env
- sed -i "s@.env@@g" .gitignore
artifacts:
paths:
- .env
- .gitignore
// .env
BASE_URL=