2015-10-14 15 views
61

Ich benutze react-native, um eine Cross-Plattform-App zu erstellen, aber ich weiß nicht, wie ich die Umgebungsvariable einstellen soll, damit ich verschiedene Konstanten für verschiedene Umgebungen haben kann.Umgebungsvariable in reaktiv-nativ setzen?

Beispiel:

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

Sie können versuchen, diese 'import {Plattform} von 'reaktiv-native';' 'console.log (Plattform);' –

Antwort

63

Statt Hartcodierung Ihrer App Konstanten und auf der Umwelt einen Schalter zu tun (ich werde erklären, wie das in einem Moment zu tun), ich schlage vor, mit dem twelve factor Vorschlag Buildprozess definiert von mit Ihrem BASE_URL und Ihrem API_KEY .

Um zu antworten, wie Sie Ihre Umgebung react-native aussetzen, empfehle ich die Verwendung von Babel's babel-plugin-transform-inline-environment-variables.

Um dies zu erhalten arbeiten Sie das Plugin herunterladen müssen und dann werden Sie zu Setup benötigen eine .babelrc und es sollte wie folgt aussehen:

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

Und so, wenn Sie transpile Ihre reagieren nativen Code, indem Sie API_KEY=my-app-id react-native bundle (oder starten, run-ios oder Lauf android) dann alles, was Sie tun müssen, ist Ihr Code aussehen wie dieses:

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

und dann wird Babel ersetzen, dass mit:

const apiKey = 'my-app-id'; 

Hoffe, das hilft!

+1

Ich habe nur das Plugin "transform-inline-environment-variables" verwendet. Sie scheinen den Fehler Reactive Native behoben zu haben. – Scimonster

+0

Ich ging voran und aktualisierte meine Antwort. Danke @Scimonster! – chapinkapa

+0

Dies scheint nicht in 0.36 zu funktionieren. Die Vars werden einfach nie gelesen. – duhseekoh

17

nativen reagiert nicht das Konzept der globalen Variablen. Erzwingt modular scope streng, um Modularität und Wiederverwendbarkeit der Komponenten zu fördern.

Manchmal benötigen Sie jedoch Komponenten, um sich ihrer Umgebung bewusst zu sein. In diesem Fall ist es sehr einfach, eine Environment Modul zu definieren, welche Komponenten dann nennen können Umgebungsvariablen zu erhalten, zum Beispiel:

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 

Dadurch wird eine singleton Umgebung erstellt, auf die Sie von überall innerhalb des Anwendungsbereichs Ihrer App zugreifen können. Sie müssen explizit das Modul require(...) aus beliebigen Komponenten, die Umgebungsvariablen verwenden, aber das ist eine gute Sache.

+11

mein Problem ist, wie man 'getPlatform()'. Ich habe eine Datei wie diese erstellt, kann aber die Logik hier in React Native –

+0

@DamonYuan nicht beenden, die vollständig davon abhängt, wie Sie Ihre Pakete einrichten. Ich habe keine Ahnung, was "Inszenierung" oder "Produktion" bedeutet, denn es hängt von Ihrer Umgebung ab. Wenn Sie beispielsweise unterschiedliche Varianten für IOS und Android möchten, können Sie die Umgebung initialisieren, indem Sie sie in Ihre Dateien 'index.ios.js' und' index.android.js' importieren und die Plattform dort einstellen, z. 'Environment.initialize ('android')'. – tohster

+0

thx, lassen Sie mich zuerst versuchen –

2

Ich denke, etwas wie die folgende Bibliothek könnte Ihnen helfen, das fehlende Teil des Puzzles, die Funktion getPlatform() zu lösen.

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

Das einzige Problem, das ich mit diesem zu sehen, dass es Asynchron-Code. Es gibt eine Pull-Anfrage, um getSync zu unterstützen. Sieh es dir auch an.

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

+0

Ich liebe die "Womp womp" lol! – chapinkapa

+3

Upvoted für die Bereitstellung eines alternativen Ansatzes nicht erwähnt. Keine einzige Größe passt für alle. –

+0

Der asynch pull req wurde in – jcollum

4

Die spezifische Methode von CI-Dienst setzen Umgebungsvariablen verwendet variieren, bauen Ansatz, Plattform und Tools Sie verwenden.

Wenn Sie Buddybuild für CI verwenden, um eine Anwendung zu erstellen und manage environment variables, und Sie müssen Zugriff auf Config von JS, eine env.js.example mit Schlüssel (mit leeren String-Werte) erstellen für den Check-in zu Quellcodeverwaltung, und verwenden Sie Buddybuild

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

Tipp: eine env.js Datei bei der Erstellung im post-clone Schritt versteckt die Dateiinhalte aus den Build-Protokollen, wie so zu produzieren nicht env.js-.gitignore hinzufügen vergessen so config und Geheimnisse aren Während der Entwicklung versehentlich in die Quellcodeverwaltung eingecheckt.

Sie können dann verwalten, wie die Datei geschrieben wird mit der Buddybuild variables wie BUDDYBUILD_VARIANTS, zum Beispiel, um eine bessere Kontrolle darüber zu bekommen, wie Ihre Konfig zur Build-Zeit produziert wird.

+0

Insgesamt mag ich die Idee, aber wie funktioniert das 'env.js.example' Teil? Nehmen wir an, ich möchte die App in meiner lokalen Umgebung starten. Wenn meine 'env.js'-Datei in Gitignore ist und' env.js.example' als Umriss verwendet wird, ist 'env.js.example' keine legale JS-Erweiterung, daher bin ich nur ein wenig verwirrt Was meinst du mit diesem Teil? – volk

+0

@volk Die 'env.js.example' Datei befindet sich in der Codebasis als Referenzdokument, eine kanonische Quelle der Wahrheit darüber, welche Konfigurationsschlüssel die App verwenden möchte. Es beschreibt sowohl die Schlüssel, die zum Ausführen der App benötigt werden, als auch den Dateinamen, der nach dem Kopieren und Umbenennen erwartet wird. Das Muster ist in Ruby-Apps üblich, die das [Dotenv-Juwel] verwenden (https://github.com/bkeepers/dotenv), wo ich das Muster aufgehoben habe. –

0

Sie auch verschiedene env-Skripte haben: production.env.sh development.env.sh production.env.sh

Und dann beziehen sie in beim Start zu arbeiten [die nur an einen Alias ​​gebunden ist] so dass alle die sh-Datei ist der Export für jede Umgebungsvariable:

export SOME_VAR=1234 
export SOME_OTHER=abc 

Und dann das Hinzufügen babel-Plugin-Transformations-Inline-Umwelt-Variablen ermöglichen den Zugriff sie im Code:

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

Fügen Sie etwas hinzu, was @chapinkapa nicht gesagt hat? –

9

Meiner Meinung nach ist die beste Option, react-native-config zu verwenden. Es unterstützt 12 factor.

Ich fand dieses Paket sehr nützlich. Sie können mehrere Umgebungen festlegen, z. Entwicklung, Inszenierung, Produktion.

Bei Android sind Variablen auch in Java-Klassen, Gradle, AndroidManifest.xml usw. verfügbar. Bei iOS sind Variablen auch in Obj-C-Klassen, Info.plist, verfügbar.

Sie erstellen nur Dateien wie

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

Sie diese Dateien mit Schlüssel füllen, Werte wie

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

und dann einfach verwenden:

import Config from 'react-native-config' 

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

Wenn Sie verschiedene Umgebungen verwenden möchten, können Sie im Grunde setzen ENVFILE Variable wie folgt aus:

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

oder für App für die Produktion Montage (in meinem Fall android):

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

Es mag erwähnenswert sein, dass es in der README heißt _Beachten Sie, dass dieses Modul keine Geheimnisse für die Verpackung verschleiert oder verschlüsselt, also speichern Sie keine sensiblen Schlüssel in .env. Es ist im Grunde unmöglich zu verhindern, dass Benutzer Reverse-Engineering-Geheimnisse der mobilen App, also Ihre App (und APIs) mit dieser Absicht gestalten – Marklar

+0

Thing ist, wird es nicht mit einigen Frameworks wie Twitter, die sie Schlüssel als com.twitter.sdk gesetzt müssen .android.CONSUMER_KEY in Ihrer .env –

+0

Wenn Sie meinen, den Schlüssel in das Manifest zu setzen, wird er von der Erweiterung unterstützt. Es ist einfach nicht in dieser Antwort beschrieben. Sie können die Variablen in XML-, Java- und JS-Dateien verwenden. – sfratini

0

@ chapinkapas Antwort ist gut. Ein Ansatz, den ich seit Mobile Center genommen haben keine Umgebungsvariablen unterstützt, ist Build-Konfiguration über eine native Modul zu belichten:

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

oder auf 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] 
    } 

Sie können die Build-Konfiguration synchron lesen und in Javascript entscheiden, wie Sie sich verhalten werden.

6

Ich verwendete die __DEV__ Polyfill, die in reactive-native eingebaut ist, um dieses Problem zu lösen. Es wird automatisch auf true gesetzt, solange Sie nicht für die Produktion nativ reagieren.

Z. B .:

//vars.js 

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

export {url, publicKey} 

Dann einfach import {url} from '../vars' und Sie werden immer die richtige bekommen. Leider funktioniert das nicht, wenn Sie mehr als zwei Umgebungen benötigen, aber es ist einfach und erfordert nicht das Hinzufügen weiterer Abhängigkeiten zu Ihrem Projekt.

1

Ich benutze babel-plugin-transform-inline-Umgebungsvariablen.

Was ich getan habe, war eine Konfigurationsdateien in S3 mit meinen verschiedenen Umgebungen.

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

EACH env-Datei:

FIRSTENV=FIRSTVALUE 
SECONDENV=SECONDVALUE 

Danach habe ich ein neues Skript in meinem Paket.json die

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 

Innerhalb der App zum Bündeln ein Skript läuft werden Sie wahrscheinlich eine Config-Datei haben, die hat:

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

, die durch babel ersetzt werden:

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

REMEMBER Sie müssen process.env ['STRING'] NOT process.env.STRING verwenden oder es wird nicht korrekt konvertieren.

+0

'REMEMBER müssen Sie process.env ['STRING'] NICHT process.env.STRING oder es wird nicht richtig konvertieren. ' Vielen Dank! Das ist derjenige, der mich stolpert !!! –

1

Ich habe eine Pre-Build-Skript für das gleiche Problem geschaffen, weil ich einige differents api Endpunkte für die differents Umgebungen benötigen

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

und ich habe eine benutzerdefinierte erstellt npm run scriptsreagieren-native Lauf auszuführen ..

Meine Paket-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", 
    ... 
} 

Dann in meinen Diensten Komponenten s implizieren Import die automatische generierte Datei:

import config from '../constants/config' 

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

Die einfachste (nicht die besten oder ideal) Lösung, die ich fand, war react-native-dotenv zu verwenden. Sie fügen einfach die "reagieren-native-dotenv" Preset Ihre .babelrc Datei im Projektstamm wie folgt:

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

erstellen .env Datei und fügen Sie Eigenschaften:

echo "SOMETHING=anything" > .env 

dann in Ihrem Projekt (JS):

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

Ich hatte auf eine .env-basierte Lösung gehofft. Vielen Dank! –

+1

@Slavo Vojacek Wie benutze ich das um zB eine 'base_url' für' staging' und 'production' zu konfigurieren? –

1

Es ist möglich, die Variablen mit process.env.blabla statt process.env['blabla'] zuzugreifen. Ich habe es kürzlich funktioniert und kommentiert, wie ich es bei einem Problem auf GitHub gemacht habe, weil ich einige Probleme mit dem Cache hatte, basierend auf der akzeptierten Antwort. Here ist das Problem.

Verwandte Themen