Встановлення змінної середовища у реальній реакції?


152

Я використовую рідну реакцію для створення додатку для різних платформ, але я не знаю, як встановити змінну середовища, щоб я могла мати різні константи для різних середовищ.

Приклад:

development: 
  BASE_URL: '',
  API_KEY: '',
staging: 
  BASE_URL: '',
  API_KEY: '',
production:
  BASE_URL: '',
  API_KEY: '',

ви можете спробувати цеimport {Platform} from 'react-native'; console.log(Platform);
Praveen Prasad

Відповіді:


138

Замість того, щоб жорстко кодувати константи програми та робити перемикання на середовище (я поясню, як це зробити за мить), я пропоную використовувати дванадцять факторів, щоб ваш процес збирання визначив ваш BASE_URLта ваш API_KEY.

Щоб відповісти, як піддавати своє оточення react-native, я пропоную використовувати змінні Babel -plugin-plugin-transform-inline-environment-змінної .

Щоб зробити це робочим, вам потрібно завантажити плагін, після чого вам потрібно буде встановити a, .babelrcі він повинен виглядати приблизно так:

{
  "presets": ["react-native"],
  "plugins": [
    "transform-inline-environment-variables"
  ]
}

І тому, якщо ви транспілюєте свій початковий код реакції, запустивши API_KEY=my-app-id react-native bundle(або запустивши, запустіть-ios або запустив-android), тоді все, що вам потрібно зробити, - це щоб ваш код виглядав так:

const apiKey = process.env['API_KEY'];

І тоді Вавілон замінить це на:

const apiKey = 'my-app-id';

Сподіваюся, це допомагає!


7
Здається, це чудове рішення, але не працює для мене за адресою RN@0.37.0. Єдине властивість на process.envце NODE_ENV.
Адам Фарина

2
Дивіться відповідь Джека Дженга нижче ... ви не можете отримати доступ до змінної через process.env.API_KEY... використовувати process.env['API_KEY']замість цього
Стівен Яп

6
Я отримую process.env ['API_KEY'] як невизначений.
Хтось

2
У мене була така ж проблема: невизначена
Гуто Маррара Марзагао

7
Для мене працює в v0.56. Ви повинні очистити кеш-пам'ять постачальника, виконуючи react-native start --reset-cacheщоразу, коли ви змінюєте змінні середовища.
soheilpro

55

Найпростішим (не найкращим чи ідеальним ) рішенням, який я знайшов, було використання react-native-dotenv . Ви просто додаєте попередньо встановлений "react-native-dotenv" у свій .babelrcфайл у корені проекту так:

{
  "presets": ["react-native", "react-native-dotenv"]
}

Створіть .envфайл та додайте властивості:

echo "SOMETHING=anything" > .env

Потім у вашому проекті (JS):

import { SOMETHING } from 'react-native-dotenv'
console.log(SOMETHING) // "anything"

1
Я сподівався на рішення на основі .env. Дякую!
Аншул Кока

3
@Slavo Vojacek Як використовувати це для налаштування, наприклад, одного base_urlдля обох stagingта production?
Compaq LE2202x

@ CompaqLE2202x Я не зовсім впевнений, що розумію? Ви запитуєте про використання різних .envфайлів (за середовищем) або про повторне використання деяких своїх значень у різних .envфайлах, щоб не дублювати їх, скажімо, на етапі та виробництві?
Славо Воячек

5
@SlavoVojacek Я запитую про різні .envфайли в середовищі, скажімо, stagingі production.
Compaq LE2202x

@SlavoVojacek не могли ви перезаписати значення на етапі CI або при розгортанні?
mgamsjager

37

На мою думку, найкращим варіантом є використання react-native-config . Він підтримує 12 фактор .

Я вважав цей пакет надзвичайно корисним. Ви можете встановити кілька середовищ, наприклад розробку, постановку, виробництво.

У випадку Android, змінні доступні також у класах Java, gradle, AndroidManifest.xml тощо. У разі iOS, змінні доступні також у класах Obj-C, Info.plist.

Ви просто створюєте такі файли, як

  • .env.development
  • .env.staging
  • .env.production

Ви заповнюєте ці файли ключовими, такими як значення

API_URL=https://myapi.com
GOOGLE_MAPS_API_KEY=abcdefgh

а потім просто скористайтеся ним:

import Config from 'react-native-config'

Config.API_URL  // 'https://myapi.com'
Config.GOOGLE_MAPS_API_KEY  // 'abcdefgh'

Якщо ви хочете використовувати різні середовища, ви в основному встановлюєте змінну ENVFILE так:

ENVFILE=.env.staging react-native run-android

або для складання програми для виробництва (андроїд в моєму випадку):

cd android && ENVFILE=.env.production ./gradlew assembleRelease

9
Можливо, варто відзначити, що в тексті README зазначено. Майте на увазі, що цей модуль не приховує та не шифрує секрети для упаковки, тому не зберігайте чутливі ключі у .env. В основному неможливо запобігти користувачам таємні секрети мобільних додатків, тому спроектуйте свій додаток (та API) з цим на увазі
Marklar

Справа в тому, що вона не працюватиме з деякими рамками, такими як твіттер, який вимагає, щоб вони були встановлені як com.twitter.sdk.android.CONSUMER_KEY у вашому .env
thibaut noah

Якщо ви маєте на увазі введення ключа всередину Manifest, розширення підтримує його. Він просто не описаний у цій відповіді. Ви можете використовувати змінні у файлах XML, Java та JS.
sfratini

4
react-native-config не працює з RN 0,56, у нього є невирішені проблеми, і він не підтримується більше 6 місяців. Проблема відьми вбиває її використання в RN - це github.com/luggit/react-native-config/isissue/267 , ось деякі злому, щоб змусити це працювати github.com/luggit/react-native-config/isissue/285
Marecky

24

React native не має поняття глобальних змінних. Він суворо застосовує модульну сферу , щоб сприяти модульності модулів та їх повторному використанню.

Однак іноді потрібні компоненти, щоб знати про їх оточення. У цьому випадку дуже просто визначити Environmentмодуль, який компоненти можуть потім викликати, щоб отримати змінні середовища, наприклад:

environment.js

var _Environments = {
    production:  {BASE_URL: '', API_KEY: ''},
    staging:     {BASE_URL: '', API_KEY: ''},
    development: {BASE_URL: '', API_KEY: ''},
}

function getEnvironment() {
    // Insert logic here to get the current platform (e.g. staging, production, etc)
    var platform = getPlatform()

    // ...now return the correct environment
    return _Environments[platform]
}

var Environment = getEnvironment()
module.exports = Environment

my-komponent.js

var Environment = require('./environment.js')

...somewhere in your code...
var url = Environment.BASE_URL

Це створює одиночне середовище, доступ до якого можна отримати з будь-якого місця в межах програми. Ви повинні явно require(...)модуль з будь-яких компонентів, які використовують змінні середовища, але це добре.


19
моя проблема - як це зробити getPlatform(). Я створив такий файл, але не можу закінчити логіку тут, у React Native
Деймон Юань

@DamonYuan, що повністю залежить від того, як ви налаштовуєте ваші пакети. Я поняття не маю, що stagingабо productionнавіть означати, тому що це залежить від вашого оточення. Наприклад, якщо ви хочете різні аромати для IOS проти Android , то ви можете форматувати навколишнє середовище за рахунок його імпорту ваших index.ios.jsі index.android.jsфайлів і настройки платформи там, наприклад Environment.initialize('android').
Тостер

@DamonYuan робить те, що мені взагалі допомогти, чи тобі потрібно ще щось уточнення?
chapinkapa

Це дуже приємно, коли ти маєш контроль над кодом. Я запускаю модуль третьої частини, який спирається на process.env, тому ...
enapupe

2
Якщо ви створюєте env.jsфайл, не забудьте проігнорувати його від реєстрації до сховища та скопіюйте використовувані ключі із порожніми рядковими значеннями в інший env.js.exampleфайл, у якому ви робите реєстрацію, щоб інші могли легше створити ваш додаток. Якщо ви випадково перевірте секрети проекту, розгляньте можливість переписання історії, щоб видалити їх не лише з джерела, але й з історії.
Джош Хабдас

17

__DEV__Для вирішення цієї проблеми я використав поліфіл, який вбудований у реактивну реакцію. Він автоматично встановлюється до trueтих пір, поки ви не будуєте реагувати на виробництво.

Наприклад:

//vars.js

let url, publicKey;
if (__DEV__) {
  url = ...
  publicKey = ...
} else {
  url = ...
  publicKey = ...
}

export {url, publicKey}

Тоді просто import {url} from '../vars'і ви завжди отримаєте правильний. На жаль, ця робота не працює, якщо ви хочете більше двох середовищ, але це просто і не передбачає додавання більшої кількості залежностей у ваш проект.


чи знаєте ви спосіб "примусити" DEV до ІСТИННОГО, навіть коли створюєте збірку релізів у xcode?
realtebo

1
Ні. Я просто прокоментую prod vars, а потім скопіюйте вставити dev vars у розділ prod, коли я хочу зробити збірку релізів із змінними dev.
Журнал

1
Я вважав це найелегантнішим рішенням
Дані Ш90

5

Конкретний метод, який використовується для встановлення змінних оточуючих середовищ, буде залежати від служби CI, підходу до побудови, платформи та інструментів, які ви використовуєте.

Якщо ви використовуєте Buddybuild for CI для створення програми та керування змінними середовища , і вам потрібен доступ до конфігурації з JS, створіть env.js.exampleклавіші (з порожніми рядковими значеннями) для реєстрації в керуванні джерелом та використовуйте Buddybuild для створення env.jsФайл під час збирання на post-cloneетапі, приховуючи вміст файлу з журналів збірки, наприклад:

#!/usr/bin/env bash

ENVJS_FILE="$BUDDYBUILD_WORKSPACE/env.js"

# Echo what's happening to the build logs
echo Creating environment config file

# Create `env.js` file in project root
touch $ENVJS_FILE

# Write environment config to file, hiding from build logs
tee $ENVJS_FILE > /dev/null <<EOF
module.exports = {
  AUTH0_CLIENT_ID: '$AUTH0_CLIENT_ID',
  AUTH0_DOMAIN: '$AUTH0_DOMAIN'
}
EOF

Порада: Не забудьте додати env.jsдо .gitignoreцього конфігурацію та секрети не перевіряються у керування джерелами випадково під час розробки.

Ви можете управляти , як файл буде написана з використанням змінних Buddybuild , як BUDDYBUILD_VARIANTS, наприклад, щоб отримати більший контроль над тим, як ваш конфіг проводиться в процесі побудови.


загалом мені подобається ідея, але як працює env.js.exampleчастина? скажімо, я хочу запустити додаток у моєму локальному середовищі. якщо мій env.jsфайл знаходиться в gitignore і env.js.exampleвикористовується як контур, env.js.exampleце не є законним розширенням JS, тому я просто трохи заплутався в тому, що ви мали на увазі під цією частиною
volk

@volk env.js.exampleФайл знаходиться в кодовій базі як довідковий документ, що є канонічним джерелом істини щодо того, які конфігураційні ключі додаток хоче споживати. Вони обидва описують ключі, необхідні для запуску програми, а також ім'я файлу, яке очікується після копіювання та перейменування. Візерунок часто зустрічається в додатках Ruby, використовуючи дорогоцінний камінь dotenv , саме звідки я підняв шаблон.
Джош Хабдас

3

Я думаю, що щось на зразок наступної бібліотеки може допомогти вам вирішити пропущений біт головоломки, функцію getPlatform ().

https://github.com/joeferraro/react-native-env

const EnvironmentManager = require('react-native-env');

// read an environment variable from React Native
EnvironmentManager.get('SOME_VARIABLE')
  .then(val => {
    console.log('value of SOME_VARIABLE is: ', val);

  })
  .catch(err => {
    console.error('womp womp: ', err.message);
  });

Єдина проблема, яку я бачу з цим, - це код асинхронізації. Існує запит на виклик для підтримки getSync. Перевірте це теж.

https://github.com/joeferraro/react-native-env/pull/9


3
Запропонований за надання альтернативного підходу, який не згадується. Жоден розмір не підходить усім.
Джош Хабдас

Req тягнення асинхрону було об’єднано в
jcollum

5
react-native-env не підтримує Android. В чому справа?
jcollum

3

Я створив сценарій попередньої збірки для тієї ж проблеми, тому що мені потрібні деякі кращі точки api для різних середовищ

const fs = require('fs')

let endPoint

if (process.env.MY_ENV === 'dev') {
  endPoint = 'http://my-api-dev/api/v1'
} else if (process.env.MY_ENV === 'test') {
  endPoint = 'http://127.0.0.1:7001'
} else {
  endPoint = 'http://my-api-pro/api/v1'
}

let template = `
export default {
  API_URL: '${endPoint}',
  DEVICE_FINGERPRINT: Math.random().toString(36).slice(2)
}
`

fs.writeFile('./src/constants/config.js', template, function (err) {
  if (err) {
    return console.log(err)
  }

  console.log('Configuration file has generated')
})

І я створив звичай npm run scriptsвиконувати натисненний цикл реакцій.

Мій пакет-json

"scripts": {
    "start-ios": "node config-generator.js && react-native run-ios",
    "build-ios": "node config-generator.js && react-native run-ios --configuration Release",
    "start-android": "node config-generator.js && react-native run-android",
    "build-android": "node config-generator.js && cd android/ && ./gradlew assembleRelease",
    ...
}

Потім у мої сервісні компоненти просто імпортуйте автоматично створений файл:

import config from '../constants/config'

fetch(`${config.API_URL}/login`, params)

3

Крок 1: Створіть окремий компонент, як цей Ім'я компонента: pagebase.js
Крок 2: Всередині цього коду використання це

    export const BASE_URL = "http://192.168.10.10:4848/";
    export const API_KEY = 'key_token';

Крок 3: Використовуйте його в будь-якому компоненті, для його використання спочатку імпортуйте цей компонент, а потім використовуйте. Імпортуйте його та використовуйте:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY

2

Я використовую babel-plugin-transform-inline-environment-variables.

Що я зробив, це помістити файли конфігурації в S3 з моїми різними середовищами.

s3://example-bucket/dev-env.sh
s3://example-bucket/prod-env.sh
s3://example-bucket/stage-env.sh

EACH env file:

FIRSTENV=FIRSTVALUE
SECONDENV=SECONDVALUE

Згодом я додав у свій новий сценарій, package.jsonякий виконує сценарій для пакетування

if [ "$ENV" == "production" ]
then
  eval $(aws s3 cp s3://example-bucket/prod-env.sh - | sed 's/^/export /')
elif [ "$ENV" == "staging" ]
then
  eval $(aws s3 cp s3://example-bucket/stage-env.sh - | sed 's/^/export /')
else
  eval $(aws s3 cp s3://example-bucket/development-env.sh - | sed 's/^/export /')
fi

react-native start

У вашому додатку, ймовірно, буде файл конфігурації, який містить:

const FIRSTENV = process.env['FIRSTENV']
const SECONDENV = process.env['SECONDENV']

який буде замінений вавилоном на:

const FIRSTENV = 'FIRSTVALUE'
const SECONDENV = 'SECONDVALUE'

ПАМ’ЯТАЙТЕ, ви повинні використовувати process.env['STRING']НЕ, process.env.STRINGабо він не буде конвертувати належним чином.


REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.Дякую! Це той, який мене подорожує !!!
Стівен Яп

1

[Джерело] З того, що я знайшов, це виглядає як за замовчуванням, можливо робити лише конфігурації виробництва та розробки (без інсценізації чи інших середовищ) - це правильно?

Зараз я використовую файл Environment.js, який можна використовувати для виявлення каналів випуску expo та зміни змінних, що повертаються на основі цього, але для побудови мені потрібно оновити змінну non-DEV, яка повертається, або стадійною, або prod:

import { Constants } from 'expo';
import { Platform } from 'react-native';
const localhost = Platform.OS === 'ios' ? 'http://localhost:4000/' : 'http://10.0.2.2:4000/';
const ENV = {
  dev: {
    apiUrl: localhost,
  },
  staging: {
    apiUrl: 'https://your-staging-api-url-here.com/'
  },
  prod: {
    apiUrl: 'https://your-prod-api-url-here.com/'
  },
}
const getEnvVars = (env = Constants.manifest.releaseChannel) => {
  // What is __DEV__ ?
  // This variable is set to true when react-native is running in Dev mode.
  // __DEV__ is true when run locally, but false when published.
  if (__DEV__) {
    return ENV.dev;
  } else {
    // When publishing to production, change this to `ENV.prod` before running an `expo build`
    return ENV.staging;
  }
}
export default getEnvVars;

Альтернативи

хтось має досвід використання react-native-dotenv для проектів, побудованих за допомогою expo? Я хотів би почути ваші думки

https://github.com/zetachang/react-native-dotenv


Ви можете визначити стільки імен каналів випуску, скільки вам потрібно, і протестувати ім'я, щоб визначити змінну середовища. Там, де я бачу, обмеження знаходиться в середовищі розробників, де випускChannel не визначений. Тож, можливо, ви можете використовувати змінні babel-plugin-transform-inline-environment-змінних - ви можете передати змінні середовища у своїх сценаріях та довідковому файлі process.env ['VAR_NAME'] у вашому файлі Environment.js, якщо dev?
colemerrick

0

Ви також можете мати різні сценарії env: production.env.sh development.env.sh production.env.sh

А потім джерело їх, коли вони починають працювати [який просто пов'язаний з псевдонімом], тому весь файл sh має експорт для кожної змінної env:

export SOME_VAR=1234
export SOME_OTHER=abc

І тоді додавання змінних babel-plugin-transform-inline-environment дозволить отримати доступ до них у коді:

export const SOME_VAR: ?string = process.env.SOME_VAR;
export const SOME_OTHER: ?string = process.env.SOME_OTHER;

Ви додаєте щось, що @chapinkapa не сказав?
Максимо Домінгес

0

@ chapinkapa відповідь хороша. Підхід, який я застосував, оскільки Mobile Center не підтримує змінні середовища, полягає у викритті конфігурації збірки за допомогою власного модуля:

На android:

   @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        String buildConfig = BuildConfig.BUILD_TYPE.toLowerCase();
        constants.put("ENVIRONMENT", buildConfig);
        return constants;
    } 

або на ios:

  override func constantsToExport() -> [String: Any]! {
    // debug/ staging / release
    // on android, I can tell the build config used, but here I use bundle name
    let STAGING = "staging"
    let DEBUG = "debug"

    var environment = "release"
    if let bundleIdentifier: String = Bundle.main.bundleIdentifier {
      if (bundleIdentifier.lowercased().hasSuffix(STAGING)) {
        environment = STAGING
      } else if (bundleIdentifier.lowercased().hasSuffix(DEBUG)){
        environment = DEBUG
      }
    }

    return ["ENVIRONMENT": environment]
  }

Ви можете прочитати конфігурацію збірки синхронно і вирішити в Javascript, як ви будете вести себе.


0

Доступ до змінних можна отримати process.env.blablaзамість process.env['blabla']. Нещодавно я змусив це працювати і прокоментував, як я це робив у питанні на GitHub, оскільки у мене виникли деякі проблеми з кешем на основі прийнятої відповіді. Ось питання.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.