Site icon May's Notes

React Native 奇幻之旅(18)-環境變數的管理(env)

React Native logo

有些時候我們會需要針對不同的環境設置不同的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:

  API_URL=http://192.168.0.xxx
  API_URL=https://dev.api.xxxxxxx
  API_URL=https://api.xxxxxxx

記得要將 .env, .env.dev, .env.prod 加到 .gitignore 中。

接著在 package.json 中新增不同的啟動 script:

{
  "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(含)以上

// .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:

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=

參考資料

Exit mobile version