Ustawiasz zmienną środowiskową w React-Native?

152

Do tworzenia aplikacji wieloplatformowych używam native speakera, ale nie wiem, jak ustawić zmienną środowiskową, aby mieć różne stałe dla różnych środowisk.

Przykład:

development: 
  BASE_URL: '',
  API_KEY: '',
staging: 
  BASE_URL: '',
  API_KEY: '',
production:
  BASE_URL: '',
  API_KEY: '',
Damon Yuan
źródło
możesz tego spróbowaćimport {Platform} from 'react-native'; console.log(Platform);
Praveen Prasad

Odpowiedzi:

138

Zamiast na stałe kodować stałe aplikacji i przełączać środowisko (za chwilę wyjaśnię, jak to zrobić), sugeruję skorzystanie z dwunastoczynnikowej sugestii, aby proces tworzenia definiował Twoje BASE_URLi Twoje API_KEY.

Aby odpowiedzieć na pytanie, jak udostępnić swoje środowisko react-native, sugeruję użycie zmiennych babel-plugin-transform-inline-environment-zmiennych firmy Babel .

Aby to działało, musisz pobrać wtyczkę, a następnie musisz skonfigurować plik .babelrci powinien wyglądać mniej więcej tak:

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

Jeśli przetransponujesz swój natywny kod, uruchamiając API_KEY=my-app-id react-native bundle(lub uruchamiając, run-ios lub run-android), wszystko, co musisz zrobić, to sprawić, by kod wyglądał następująco:

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

A potem Babel zastąpi to:

const apiKey = 'my-app-id';

Mam nadzieję że to pomoże!

chapinkapa
źródło
7
Brzmi jak świetne rozwiązanie, ale nie działa dla mnie przy [email protected]. Jedyna właściwość na process.envto NODE_ENV.
Adam Faryna
2
Zobacz odpowiedź poniżej przez Jacka Zhenga ... nie możesz uzyskać dostępu do zmiennej przez process.env.API_KEY... użyj process.env['API_KEY']zamiast tego
Steven Yap
6
Otrzymuję process.env ['API_KEY'] jako niezdefiniowany. Czy ktoś może mi pomóc w konfiguracji
user1106888
2
Miałem ten sam problem: nieokreślony
Guto Marrara Marzagao
7
U mnie działa w wersji 0.56. Musisz wyczyścić pamięć podręczną bundlera, uruchamiając za react-native start --reset-cachekażdym razem, gdy zmieniasz zmienne środowiskowe.
soheilpro
55

Najprostszym (nie najlepszym ani idealnym ) rozwiązaniem, jakie znalazłem, było użycie reakcji-native-dotenv . Po prostu dodajesz ustawienie „react-native-dotenv” do swojego .babelrcpliku w katalogu głównym projektu w następujący sposób:

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

Utwórz .envplik i dodaj właściwości:

echo "SOMETHING=anything" > .env

Następnie w Twoim projekcie (JS):

import { SOMETHING } from 'react-native-dotenv'
console.log(SOMETHING) // "anything"
Slavo Vojacek
źródło
1
Liczyłem na rozwiązanie oparte na .env. Dziękuję Ci!
Anshul Koka
3
@Slavo Vojacek Jak mogę tego użyć, aby skonfigurować na przykład jeden base_urldla obu stagingi production?
Compaq LE2202x
@ CompaqLE2202x Nie jestem do końca pewien, czy rozumiem? Czy pytasz o używanie różnych .envplików (na środowisko) lub o ponowne wykorzystanie niektórych wartości w różnych .envplikach, aby nie powielać ich między, powiedzmy, etapami i produkcją?
Slavo Vojacek
5
@SlavoVojacek Pytam o różne .envpliki w zależności od środowiska, powiedzmy stagingi production.
Compaq LE2202x
@SlavoVojacek nie mógłbyś nadpisać wartości na etapie CI lub podczas wdrażania?
mgamsjager
37

Moim zdaniem najlepszą opcją jest użycie react-native-config . Obsługuje 12 czynników .

Uważam, że ten pakiet jest niezwykle przydatny. Możesz ustawić wiele środowisk, np. Programowanie, staging, produkcja.

W przypadku Androida zmienne dostępne są również w klasach Java, gradle, AndroidManifest.xml itp. W przypadku iOS zmienne dostępne są także w klasach Obj-C Info.plist.

Po prostu tworzysz pliki takie jak

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

Wypełniasz te pliki kluczami, wartościami takimi jak

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

a potem po prostu go użyj:

import Config from 'react-native-config'

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

Jeśli chcesz używać różnych środowisk, po prostu ustawiasz zmienną ENVFILE w następujący sposób:

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

lub do składania aplikacji na produkcję (w moim przypadku Android):

cd android && ENVFILE=.env.production ./gradlew assembleRelease
Patrik Prevuznak
źródło
9
Warto zauważyć, że w README jest napisane Pamiętaj, że ten moduł nie zaciemnia ani nie szyfruje sekretów do pakowania, więc nie przechowuj wrażliwych kluczy w .env. Zasadniczo niemożliwe jest uniemożliwienie użytkownikom odwrotnej inżynierii tajemnic aplikacji mobilnych, więc zaprojektuj swoją aplikację (i interfejsy API) mając to na uwadze
Marklar
Chodzi o to, że nie będzie działać z niektórymi frameworkami, takimi jak Twitter, co wymaga ustawienia klucza jako com.twitter.sdk.android.CONSUMER_KEY w twoim .env
noah
Jeśli masz na myśli umieszczenie klucza wewnątrz manifestu, rozszerzenie go obsługuje. Po prostu nie jest to opisane w tej odpowiedzi. Możesz używać zmiennych w plikach XML, Java i JS.
sfratini
4
React-native-config nie działa z RN 0.56, ma nierozwiązane problemy i nie jest obsługiwany przez ponad 6 miesięcy. Problem, który czarownica zabija jego użycie w RN, to github.com/luggit/react-native-config/issues/267 , oto kilka hacków, aby to działało github.com/luggit/react-native-config/issues/285
Marecky
24

React native nie ma pojęcia zmiennych globalnych. Egzekwuje ściśle zakres modułowy , aby promować modułowość komponentów i możliwość ich ponownego wykorzystania.

Czasami jednak potrzebujesz komponentów, aby mieć świadomość swojego otoczenia. W tym przypadku bardzo łatwo jest zdefiniować Environmentmoduł, którego komponenty mogą następnie wywoływać w celu pobrania zmiennych środowiskowych, na przykład:

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-component.js

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

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

Tworzy to pojedyncze środowisko, do którego można uzyskać dostęp z dowolnego miejsca w zakresie aplikacji. Musisz jawnie require(...)określić moduł z dowolnych komponentów, które używają zmiennych środowiskowych, ale to dobrze.

tohster
źródło
19
mój problem polega na tym, jak to zrobić getPlatform(). Zrobiłem taki plik, ale nie mogę dokończyć logiki tutaj w React Native
Damon Yuan
@DamonYuan to zależy całkowicie od tego, jak konfigurujesz swoje pakiety. Nie mam pojęcia, co staginglub productionnawet średnią, bo to zależy od środowiska. Na przykład, jeśli chcesz mieć różne smaki dla IOS vs Android, możesz zainicjować Środowisko, importując je index.ios.jsi index.android.jspliki i ustawiając tam platformę, np Environment.initialize('android').
tohster
@DamonYuan robi w ogóle to, co umieściłem, czy potrzebujesz więcej wyjaśnień?
chapinkapa
Jest to bardzo przyjemne, gdy masz kontrolę nad kodem. Uruchamiam moduł trzeciej części, który opiera się na process.env, więc ...
enapupe
2
Jeśli tworzysz env.jsplik, pamiętaj, aby zignorować go od wyewidencjonowania do repozytorium i skopiować używane klucze, z pustymi wartościami ciągów, do innego env.js.examplepliku, który rejestrujesz, aby inni mogli łatwiej zbudować Twoją aplikację. Jeśli przypadkowo sprawdzisz sekrety projektu, rozważ przepisanie historii, aby usunąć je nie tylko ze źródła, ale także z historii.
Josh Habdas
17

__DEV__Aby rozwiązać ten problem, użyłem wypełniacza wbudowanego w React-Native. Jest automatycznie ustawiana truetak długo, jak długo nie tworzysz natywnego reagowania na produkcję.

Na przykład:

//vars.js

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

export {url, publicKey}

Wtedy po prostu, import {url} from '../vars'a zawsze otrzymasz właściwy. Niestety, to nie zadziała, jeśli chcesz mieć więcej niż dwa środowiska, ale jest łatwe i nie wymaga dodawania większej liczby zależności do projektu.

Logister
źródło
Czy znasz sposób na „foce” DEV do TRUE nawet podczas tworzenia kompilacji wydania w xcode?
realtebo
1
Nie. Po prostu komentuję vars prod, a następnie kopiuję wklej vars dev do sekcji prod, gdy chcę zrobić kompilację wydania ze zmiennymi dev.
Logister
1
Uważam, że to najbardziej eleganckie rozwiązanie
Dani Sh90
5

Konkretna metoda używana do ustawiania zmiennych środowiskowych będzie się różnić w zależności od usługi CI, podejścia do kompilacji, platformy i używanych narzędzi.

Jeśli używasz Buddybuild for CI do tworzenia aplikacji i zarządzania zmiennymi środowiskowymi i potrzebujesz dostępu do konfiguracji z JS, utwórz env.js.exampleklucz z kluczami (z pustymi wartościami ciągu) do wpisywania do kontroli źródła i użyj Buddybuild, aby utworzyć env.jsplik w czasie kompilacji w post-clonekroku, ukrywając zawartość pliku w dziennikach kompilacji, na przykład:

#!/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

Wskazówka: nie zapomnij dodać env.jsdo, .gitignorewięc config i wpisy tajne nie są przypadkowo sprawdzane w kontroli źródła podczas programowania.

Następnie można zarządzać jak plik zostanie napisane przy użyciu zmiennych Buddybuild jak BUDDYBUILD_VARIANTS, na przykład, aby uzyskać większą kontrolę nad tym, jak twój config jest produkowany w czasie kompilacji.

Josh Habdas
źródło
ogólnie podoba mi się ten pomysł, ale jak działa ta env.js.exampleczęść? powiedzmy, że chcę uruchomić aplikację w moim środowisku lokalnym. jeśli mój env.jsplik jest w gitignore i env.js.examplejest używany jako konspekt, env.js.examplenie jest to poprawne rozszerzenie JS, więc jestem trochę zdezorientowany, co masz na myśli przez tę część
volk
@volk env.js.examplePlik znajduje się w bazie kodu jako dokument referencyjny, kanoniczne źródło informacji o tym, jakie klucze konfiguracyjne aplikacja chce wykorzystać. Oba opisują klucze wymagane do uruchomienia aplikacji, a także nazwę pliku oczekiwaną po skopiowaniu i zmianie nazwy. Wzorzec jest powszechny w aplikacjach Ruby używających klejnotu dotenv , z którego wziąłem wzór.
Josh Habdas
3

Myślę, że coś takiego jak poniższa biblioteka może pomóc w rozwiązaniu brakującego fragmentu układanki, funkcji 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);
  });

Jedyny problem, jaki widzę z tym, to kod asynchroniczny. Istnieje żądanie ściągnięcia do obsługi getSync. Sprawdź też.

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

leonfs
źródło
3
Głosowano za zapewnieniem alternatywnego podejścia, o którym nie wspomniano. Nie ma jednego rozmiaru dla wszystkich.
Josh Habdas,
Żądanie
5
Reakcja-native-env nie obsługuje Androida. Jaki jest sens?
jcollum
3

Stworzyłem skrypt przed kompilacją dla tego samego problemu, ponieważ potrzebuję kilku różnych punktów końcowych API dla różnych środowisk

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')
})

I stworzyłem niestandardowy, npm run scriptsaby wykonać reakcję natywną.

Mój pakiet-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",
    ...
}

Następnie w moich komponentach usług po prostu zaimportuj automatycznie wygenerowany plik:

import config from '../constants/config'

fetch(`${config.API_URL}/login`, params)
Toni Chaz
źródło
3

Krok 1: Utwórz oddzielny komponent, taki jak ten Nazwa komponentu: pagebase.js
Krok 2: Wewnątrz tego użyj kodu this

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

Krok 3: Użyj go w dowolnym komponencie, aby go użyć, najpierw zaimportuj ten komponent, a następnie użyj go. Zaimportuj i używaj:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY
Jitendra Suthar
źródło
2

Używam babel-plugin-transform-inline-environment-variables.

To, co zrobiłem, to umieszczenie plików konfiguracyjnych w S3 z różnymi środowiskami.

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

KAŻDY plik env:

FIRSTENV=FIRSTVALUE
SECONDENV=SECONDVALUE

Następnie dodałem nowy skrypt do mojego, package.jsonktóry uruchamia skrypt do sprzedaży wiązanej

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

W swojej aplikacji prawdopodobnie będziesz mieć plik konfiguracyjny, który zawiera:

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

który zostanie zastąpiony przez babel do:

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

PAMIĘTAJ, że musisz użyć process.env['STRING']NOT process.env.STRINGlub nie skonwertuje się poprawnie.

Jack Zhang
źródło
REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.Dzięki! To jest ten, który mnie podrywa !!!
Steven Yap
1

[Źródło] Z tego, co znalazłem, wygląda na to, że domyślnie jest możliwe tylko konfigurowanie produkcyjne i programistyczne (bez etapów lub innych środowisk) - czy to prawda?

W tej chwili korzystałem z pliku environment.js, który może być użyty do wykrywania kanałów wydań expo i zmiany zwracanych na ich podstawie zmiennych, ale do budowania muszę zaktualizować zmienną inną niż DEV, aby była albo tymczasowa, albo szturchać:

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;

Alternatywy

Czy ktoś ma doświadczenie w korzystaniu z React-native-dotenv w projektach budowanych za pomocą expo? Bardzo chciałbym usłyszeć Twoje myśli

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

panchicore
źródło
Możesz zdefiniować dowolną liczbę nazw kanałów wersji i przetestować nazwę w celu zdefiniowania zmiennej środowiskowej. Tam, gdzie widzę, ograniczenie znajduje się w środowisku deweloperskim, w którym releaseChannel jest niezdefiniowana. Więc może mógłbyś użyć babel-plugin-transform-inline-environment-variable - możesz przekazać zmienne środowiskowe w swoich skryptach i odwołać się do process.env ['VAR_NAME'] w swoim pliku environment.js, jeśli dev?
colemerrick
0

możesz także mieć różne skrypty env: production.env.sh development.env.sh production.env.sh

A następnie zbierz je, gdy zaczniesz pracę [co jest po prostu powiązane z aliasem], więc wszystko, co ma plik sh, jest eksportowane dla każdej zmiennej env:

export SOME_VAR=1234
export SOME_OTHER=abc

A następnie dodanie babel-plugin-transform-inline-environment-variable umożliwi dostęp do nich w kodzie:

export const SOME_VAR: ?string = process.env.SOME_VAR;
export const SOME_OTHER: ?string = process.env.SOME_OTHER;
Pikachu-go
źródło
Czy dodajesz coś, czego @chapinkapa nie powiedział?
Maximo Dominguez
0

Odpowiedź @ chapinkapa jest dobra. Podejście, które zastosowałem od czasu Mobile Center nie obsługuje zmiennych środowiskowych, polega na ujawnieniu konfiguracji kompilacji za pomocą modułu natywnego:

Na Androidzie:

   @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;
    } 

lub na 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]
  }

Możesz odczytać konfigurację kompilacji synchronicznie i zdecydować w Javascript, jak będziesz się zachowywać.

vonovak
źródło
0

Dostęp do zmiennych można uzyskać za pomocą process.env.blablazamiast process.env['blabla']. Niedawno sprawiłem, że to zadziałało i skomentowałem, jak to zrobiłem w problemie na GitHub, ponieważ miałem problemy z pamięcią podręczną na podstawie zaakceptowanej odpowiedzi. Oto problem.

Srdjan Cosic
źródło