Задать переменную окружения в реагирующем?

152

Я использую реактивную систему для создания кроссплатформенного приложения, но я не знаю, как установить переменную среды, чтобы у меня были разные константы для разных сред.

Пример:

development: 
  BASE_URL: '',
  API_KEY: '',
staging: 
  BASE_URL: '',
  API_KEY: '',
production:
  BASE_URL: '',
  API_KEY: '',
Дэймон Юань
источник
Вы можете попробовать этоimport {Platform} from 'react-native'; console.log(Platform);
Правин Прасад

Ответы:

138

Вместо того, чтобы жестко программировать константы вашего приложения и переключать среду (я сейчас объясню, как это сделать), я предлагаю использовать двенадцать факторов, чтобы ваш процесс сборки определял вас BASE_URLи ваш API_KEY.

Чтобы ответить, как представить свою среду react-native, я предлагаю использовать babel-plugin-transform-inline-environment-variable .

Чтобы это работало, вам нужно скачать плагин, а затем вам нужно будет настроить a, .babelrcи он должен выглядеть примерно так:

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

И поэтому, если вы переносите свой реактивный код с помощью запуска API_KEY=my-app-id react-native bundle(или start, run-ios или run-android), то все, что вам нужно сделать, это сделать ваш код следующим образом:

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

И тогда Бабель заменит это на:

const apiKey = 'my-app-id';

Надеюсь это поможет!

chapinkapa
источник
7
Похоже, отличное решение, но не работает для меня на RN@0.37.0. Единственное свойство на process.envэто NODE_ENV.
Адам Фарина
2
Смотрите ответ Джек Чжэнга ниже ... вы не можете получить доступ к переменной с помощью process.env.API_KEY... используйте process.env['API_KEY']вместо этого
Стивен Яп
6
Я получаю process.env ['API_KEY'] как неопределенное. Может кто-нибудь помочь мне настроить это
user1106888
2
У меня была такая же проблема: не определено
Гуто Маррара Марзагао
7
У меня работает в v0.56. Вы должны очищать кеш компоновщика, выполняя react-native start --reset-cacheкаждый раз, когда вы меняете переменные среды.
soheilpro
55

Самое простое (не самое лучшее или идеальное ) решение, которое я нашел, состояло в том, чтобы использовать response-native-dotenv . Вы просто добавляете предустановку «response-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файлах, чтобы не дублировать их, скажем, в Staging и Production?
Славо Воячек
5
@SlavoVojacek Я спрашиваю о разных .envфайлах для каждой среды, скажем, stagingи production.
Compaq LE2202x
@SlavoVojacek не могли бы вы перезаписать значения на этапе CI или при развертывании?
mgamsjager
37

На мой взгляд, лучший вариант - это использовать response-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
Если вы хотите поместить ключ в Манифест, расширение поддерживает его. Это просто не описано в этом ответе. Вы можете использовать переменные в файлах XML, Java и JS.
Сфратини
4
Reaction-native-config не работает с RN 0.56, имеет нерешенные проблемы и не поддерживается более 6 месяцев. Проблема, которая убивает его использование в RN: github.com/luggit/react-native-config/issues/267 , вот некоторые взломы, чтобы заставить его работать github.com/luggit/react-native-config/issues/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

мой-component.js

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

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

Это создает одноэлементную среду, к которой можно получить доступ из любого места в пределах области вашего приложения. Вы должны явно указать require(...)модуль из любых компонентов, которые используют переменные окружения, но это хорошо.

tohster
источник
19
моя проблема в том, как getPlatform(). Я сделал файл, как это, но не могу закончить логику здесь, в React Native
Дэймон Юань
@DamonYuan, это полностью зависит от того, как вы настраиваете свои пакеты. Я понятия не имею, что stagingили productionдаже значить, потому что это зависит от вашего окружения. Например, если вы хотите использовать разные варианты для IOS против Android, вы можете инициализировать Environment, импортировав его index.ios.jsи index.android.jsфайлы и установив там платформу, например Environment.initialize('android').
tohster
@DamonYuan делает то, что я помогал, или тебе нужно больше разъяснений?
Чапинкапа
Это очень хорошо, когда у вас есть контроль над кодом. Я использую модуль третьей части, который опирается на 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'и вы всегда получите правильный. К сожалению, это не будет работать, если вам нужно более двух сред, но это просто и не требует добавления дополнительных зависимостей в ваш проект.

Logister
источник
Знаете ли вы, как «обмануть» DEV до TRUE даже при создании релизной сборки в xcode?
Realtebo
1
Нет. Я просто закомментирую prod vars, а затем скопирую и вставлю dev dev в раздел prod, когда я хочу сделать релизную версию с переменными dev.
Logister
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

leonfs
источник
3
Голосование за предоставление альтернативного подхода не упоминается. Ни один размер не подходит всем.
Джош Хабдас
Требование асинхронного извлечения было объединено
jcollum
5
По-видимому, response-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

КАЖДЫЙ env файл:

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']

который будет заменен на babel:

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 и изменения возвращаемых переменных на основании этого, но для сборки мне нужно обновить не- DEV переменную, возвращаемую в качестве промежуточной или промежуточной. прод:

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;

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

У кого-нибудь есть опыт использования response-native-dotenv для проектов, построенных с помощью expo? Я хотел бы услышать ваши мысли

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

panchicore
источник
Вы можете определить столько имен каналов релиза, сколько захотите, и протестировать имя, чтобы определить переменную среды. Я вижу ограничение в среде разработки, где releaseChannel не определен. Поэтому, возможно, вы могли бы использовать babel-plugin-transform-inline-environment-variable - вы могли бы передавать переменные окружения в ваших скриптах и ​​ссылаться на process.env ['VAR_NAME'] в вашем файле environment.js, если dev?
Колмеррик
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-variable позволит получить к ним доступ в коде:

export const SOME_VAR: ?string = process.env.SOME_VAR;
export const SOME_OTHER: ?string = process.env.SOME_OTHER;
Пикачу-го
источник
Вы добавляете что-нибудь, что @chapinkapa не сказал?
Максимо Домингес
0

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

На андроиде:

   @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, как вы будете себя вести.

vonovak
источник
0

Можно получить доступ к переменным с помощью process.env.blablaвместо process.env['blabla']. Я недавно заставил это работать и прокомментировал, как я сделал это по проблеме на GitHub, потому что у меня были некоторые проблемы с кешем на основе принятого ответа. Здесь проблема.

Срджан Косик
источник