Mengatur variabel lingkungan di reaksi-asli?


152

Saya menggunakan reaksi asli untuk membangun aplikasi lintas platform, tetapi saya tidak tahu cara mengatur variabel lingkungan sehingga saya dapat memiliki konstanta yang berbeda untuk lingkungan yang berbeda.

Contoh:

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

Anda dapat mencoba iniimport {Platform} from 'react-native'; console.log(Platform);
Praveen Prasad

Jawaban:


138

Daripada mengkodekan konstanta aplikasi Anda dan melakukan pergantian pada lingkungan (saya akan menjelaskan bagaimana melakukannya sebentar lagi), saya sarankan menggunakan dua belas faktor saran agar proses pembangunan Anda menentukan Anda BASE_URLdan Anda API_KEY.

Untuk menjawab bagaimana mengekspos lingkungan Anda react-native, saya sarankan menggunakan variabel Babel -plugin-transform-inline-environment-variable .

Agar ini berfungsi, Anda perlu mengunduh plugin dan kemudian Anda harus menyiapkan .babelrcdan harus terlihat seperti ini:

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

Dan jika Anda mengubah kode asli-reaksi Anda dengan menjalankan API_KEY=my-app-id react-native bundle(atau memulai, menjalankan-ios, atau menjalankan-android) maka yang harus Anda lakukan adalah membuat kode Anda terlihat seperti ini:

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

Dan kemudian Babel akan menggantinya dengan:

const apiKey = 'my-app-id';

Semoga ini membantu!


7
Kedengarannya seperti solusi hebat, tetapi tidak berfungsi untuk saya di RN@0.37.0. Satu-satunya properti yang aktif process.envadalah NODE_ENV.
Adam Faryna

2
Lihat jawaban di bawah ini oleh Jack Zheng ... Anda tidak dapat mengakses variabel melalui process.env.API_KEY... gunakan process.env['API_KEY']saja
Steven Yap

6
Saya mendapatkan process.env ['API_KEY'] sebagai tidak terdefinisi. Adakah yang bisa membantu saya mengatur ini
user1106888

2
Saya memiliki masalah yang sama: tidak terdefinisi
Guto Marrara Marzagao

7
Bekerja untuk saya di v0.56. Anda harus menghapus cache bundler dengan menjalankan react-native start --reset-cachesetiap kali Anda mengubah variabel lingkungan.
soheilpro

55

Solusi paling sederhana (bukan yang terbaik atau ideal ) yang saya temukan adalah menggunakan react-native-dotenv . Anda cukup menambahkan preset "react-native-dotenv" ke .babelrcfile Anda di root proyek seperti:

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

Buat .envfile dan tambahkan properti:

echo "SOMETHING=anything" > .env

Kemudian di proyek Anda (JS):

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

1
Saya berharap untuk solusi berbasis .env. Terima kasih!
Anshul Koka

3
@Slavo Vojacek Bagaimana cara menggunakan ini untuk mengonfigurasi misalnya satu base_urluntuk keduanya stagingdan production?
Compaq LE2202x

@ CompaqLE2202x Saya tidak yakin saya mengerti? Apakah Anda bertanya tentang menggunakan .envfile yang berbeda (per lingkungan), atau tentang menggunakan kembali beberapa nilai Anda dalam .envfile yang berbeda , sehingga Anda tidak menduplikatnya, katakanlah, Pementasan dan Produksi?
Slavo Vojacek

5
@SlavoVojacek Saya bertanya tentang .envfile yang berbeda per lingkungan katakanlah stagingdan production.
Compaq LE2202x

@SlavoVojacek tidak bisakah Anda menimpa nilai dalam tahap CI atau saat penerapan?
mgamsjager

37

Menurut pendapat saya pilihan terbaik adalah menggunakan react-native-config . Ini mendukung 12 faktor .

Saya menemukan paket ini sangat berguna. Anda dapat mengatur beberapa lingkungan, misalnya pengembangan, pementasan, produksi.

Dalam hal Android, variabel tersedia juga di kelas Java, gradle, AndroidManifest.xml dll. Dalam kasus iOS, variabel tersedia juga di kelas Obj-C, Info.plist.

Anda cukup membuat file seperti

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

Anda mengisi file-file ini dengan kunci, nilai-nilai suka

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

dan kemudian gunakan saja:

import Config from 'react-native-config'

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

Jika Anda ingin menggunakan lingkungan yang berbeda, pada dasarnya Anda menetapkan variabel ENVFILE seperti ini:

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

atau untuk merakit aplikasi untuk produksi (android dalam kasus saya):

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

9
Mungkin perlu dicatat bahwa dalam README ia menyatakan Perlu diingat modul ini tidak mengaburkan atau mengenkripsi rahasia untuk kemasan, jadi jangan menyimpan kunci sensitif dalam .env. Pada dasarnya tidak mungkin untuk mencegah pengguna membalikkan rahasia aplikasi seluler, jadi rancang aplikasi Anda (dan API) dengan mengingatnya
Marklar

Masalahnya itu tidak akan bekerja dengan beberapa kerangka kerja seperti twitter yang mengharuskan mereka kunci ditetapkan sebagai com.twitter.sdk.android.CONSUMER_KEY di .env
thibaut noah Anda

Jika Anda bermaksud memasukkan kunci ke dalam Manifest, ekstensi mendukungnya. Itu tidak dijelaskan dalam jawaban ini. Anda dapat menggunakan variabel dalam file XML, Java, dan JS.
sfratini

4
react-native-config tidak bekerja dengan RN 0,56, ia memiliki masalah yang belum terselesaikan dan tidak terawat selama lebih dari 6 bulan. Penyihir masalah yang mematikan penggunaannya di RN adalah github.com/luggit/react-native-config/issues/267 , berikut ini beberapa peretasan untuk membuatnya berfungsi github.com/luggit/react-native-config/issues/285
Marecky

24

Bereaksi asli tidak memiliki konsep variabel global. Ini menegakkan lingkup modular secara ketat, untuk mempromosikan modularitas komponen dan usabilitas ulang.

Namun, kadang-kadang, Anda perlu komponen untuk mengetahui lingkungannya. Dalam hal ini sangat mudah untuk mendefinisikan Environmentmodul yang komponennya dapat dipanggil untuk mendapatkan variabel lingkungan, misalnya:

lingkungan.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

Ini menciptakan lingkungan tunggal yang dapat diakses dari mana saja di dalam lingkup aplikasi Anda. Anda harus secara eksplisit require(...)modul dari komponen apa pun yang menggunakan variabel Lingkungan, tetapi itu adalah hal yang baik.


19
masalah saya adalah bagaimana caranya getPlatform(). Saya telah membuat file seperti ini tetapi tidak dapat menyelesaikan logikanya di React Native
Damon Yuan

@DamonYuan itu sepenuhnya tergantung pada bagaimana Anda mengatur paket Anda. Saya tidak tahu apa artinya stagingatau productionbahkan, karena itu tergantung pada lingkungan Anda. Misalnya, jika Anda ingin rasa yang berbeda untuk iOS vs Android maka Anda dapat menginisialisasi Lingkungan dengan mengimpor Anda index.ios.jsdan index.android.jsfile dan pengaturan platform sana, misalnya Environment.initialize('android').
tohster

@DamonYuan melakukan apa yang saya berikan bantuan sama sekali, atau apakah Anda perlu klarifikasi lagi?
chapinkapa

Ini sangat bagus ketika Anda memiliki kendali atas kode. Saya menjalankan modul bagian ketiga yang bergantung pada process.env, jadi ...
enapupe

2
Jika Anda membuat env.jsfile, pastikan untuk mengabaikannya dari check-in ke repositori dan salin kunci yang digunakan, dengan nilai string kosong, ke env.js.examplefile lain yang Anda lakukan check-in sehingga orang lain dapat membangun aplikasi Anda dengan lebih mudah. Jika Anda secara tidak sengaja memeriksa rahasia proyek, pertimbangkan untuk menulis ulang riwayat untuk menghapusnya bukan hanya dari sumbernya tetapi juga sejarahnya.
Josh Habdas

17

Saya menggunakan __DEV__polyfill yang dibangun ke dalam reaksi-asli untuk mengatasi masalah ini. Secara otomatis diatur trueselama Anda tidak membangun reaksi asli untuk produksi.

Misalnya:

//vars.js

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

export {url, publicKey}

Maka adil import {url} from '../vars'dan Anda akan selalu mendapatkan yang benar. Sayangnya, ini tidak akan berfungsi jika Anda menginginkan lebih dari dua lingkungan, tetapi mudah dan tidak melibatkan penambahan lebih banyak ketergantungan pada proyek Anda.


apakah Anda tahu cara untuk 'membuat' DEV menjadi BENAR bahkan ketika membuat rilis build di xcode?
realtebo

1
Nggak. Saya hanya berkomentar prod vars dan kemudian salin tempel vars dev ke bagian prod ketika saya ingin melakukan rilis build dengan variabel dev.
Logister

1
Saya menemukan ini solusi paling elegan
Dani Sh90

5

Metode spesifik yang digunakan untuk mengatur variabel lingkungan akan bervariasi berdasarkan layanan CI, pendekatan pembangunan, platform dan alat yang Anda gunakan.

Jika Anda menggunakan Buddybuild untuk CI untuk membangun aplikasi dan mengelola variabel lingkungan , dan Anda memerlukan akses ke konfigurasi dari JS, buat env.js.examplekunci dengan (dengan nilai string kosong) untuk check-in ke kontrol sumber, dan gunakan Buddybuild untuk menghasilkan env.jsfile saat membangun di post-clonelangkah, menyembunyikan konten file dari log bangunan, seperti:

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

Tip: Jangan lupa untuk menambahkan env.jsuntuk .gitignorejadi config dan rahasia tidak diperiksa ke kontrol sumber sengaja selama perkembangan.

Anda kemudian dapat mengelola bagaimana file ditulis menggunakan variabel Buddybuild seperti BUDDYBUILD_VARIANTS, misalnya, untuk mendapatkan kontrol yang lebih besar tentang bagaimana konfigurasi Anda dihasilkan pada waktu build.


secara keseluruhan saya suka ide itu, tetapi bagaimana cara env.js.examplekerjanya? katakanlah saya ingin meluncurkan aplikasi di lingkungan lokal saya. jika env.jsfile saya ada di gitignore dan env.js.exampledigunakan sebagai garis besar, env.js.exampleitu bukan ekstensi JS yang sah, jadi saya hanya sedikit bingung dengan apa yang Anda maksud dengan bagian ini
volk

@volk env.js.exampleFile duduk di basis kode sebagai dokumen referensi, sumber kebenaran kanonik tentang apa kunci konfigurasi yang ingin dikonsumsi aplikasi. Keduanya menjelaskan kunci yang diperlukan untuk menjalankan aplikasi, serta nama file yang diharapkan setelah disalin dan diganti namanya. Pola ini umum di aplikasi Ruby menggunakan permata dotenv , yang merupakan asal saya mengangkat pola.
Josh Habdas

3

Saya pikir sesuatu seperti perpustakaan berikut ini dapat membantu Anda memecahkan sedikit puzzle yang hilang, fungsi 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);
  });

Satu-satunya masalah yang saya lihat dengan ini, yaitu kode async. Ada permintaan tarik untuk mendukung getSync. Lihat juga.

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


3
Terpilih untuk menyediakan pendekatan alternatif yang tidak disebutkan. Tidak ada satu ukuran yang cocok untuk semua.
Josh Habdas

Req tarikan asynch telah digabung dalam
jcollum

5
react-native-env tampaknya tidak mendukung Android. Apa gunanya?
jcollum

3

Saya telah membuat skrip pra-bangun untuk masalah yang sama karena saya memerlukan beberapa titik akhir api differents untuk lingkungan differents

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

Dan saya telah membuat kebiasaan npm run scriptsuntuk menjalankan menjalankan reaksi-asli ..

Paket-json saya

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

Kemudian dalam komponen layanan saya, cukup impor file yang dibuat secara otomatis:

import config from '../constants/config'

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

3

Langkah 1: Buat komponen terpisah seperti ini Nama komponen: pagebase.js
Langkah 2: Di dalam ini gunakan kode ini

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

Langkah 3: Gunakan di komponen apa pun, untuk menggunakannya, impor komponen ini terlebih dahulu, lalu gunakan. Impor dan gunakan:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY

2

Saya menggunakan babel-plugin-transform-inline-environment-variables.

Apa yang saya lakukan adalah meletakkan file konfigurasi dalam S3 dengan lingkungan saya yang berbeda.

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

SETIAP file env:

FIRSTENV=FIRSTVALUE
SECONDENV=SECONDVALUE

Setelah itu, saya menambahkan skrip baru di saya package.jsonyang menjalankan skrip untuk bundling

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

Di dalam aplikasi Anda, Anda mungkin memiliki file konfigurasi yang memiliki:

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

yang akan diganti oleh babel ke:

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

INGAT Anda harus menggunakan process.env['STRING']TIDAK process.env.STRINGatau itu tidak akan mengkonversi dengan benar.


REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.Terima kasih! Ini yang membuatku tersandung !!!
Steven Yap

1

[Sumber] Dari apa yang saya temukan, sepertinya secara default, hanya dimungkinkan untuk melakukan konfigurasi produksi dan pengembangan (tidak ada tahapan atau lingkungan lain) - benarkah itu?

Saat ini, saya telah menggunakan file environment.js yang dapat digunakan untuk mendeteksi saluran rilis expo dan mengubah variabel yang dikembalikan berdasarkan itu, tetapi untuk membangun, saya perlu memperbarui variabel non- DEV yang dikembalikan menjadi pementasan atau melecut:

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;

Alternatif

apakah ada yang punya pengalaman menggunakan react-native-dotenv untuk proyek yang dibangun dengan expo? Saya ingin mendengar pendapat Anda

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


Anda bisa mendefinisikan sebanyak mungkin nama saluran rilis, dan menguji nama untuk menentukan variabel lingkungan Anda. Di mana saya melihat batasannya adalah di lingkungan dev di mana releaseChannel tidak terdefinisi. Jadi mungkin Anda bisa menggunakan variabel babel-plugin-transform-inline-environment-variable - Anda bisa meneruskan variabel lingkungan di skrip dan proses referensi Anda.env ['VAR_NAME'] di file environment.js Anda jika dev?
colemerrick

0

Anda juga dapat memiliki skrip env yang berbeda: production.env.sh development.env.sh production.env.sh

Dan kemudian sumber mereka ketika mulai bekerja [yang hanya terikat ke alias] sehingga semua file sh telah diekspor untuk setiap variabel env:

export SOME_VAR=1234
export SOME_OTHER=abc

Dan kemudian menambahkan variabel -abel-plugin-transform-in-lingkungan-variabel akan memungkinkan mengaksesnya dalam kode:

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

Apakah Anda menambahkan sesuatu yang belum dikatakan @chapinkapa?
Maximo Dominguez

0

@ chapinkapa jawabannya bagus. Pendekatan yang saya ambil sejak Mobile Center tidak mendukung variabel lingkungan, adalah mengekspos konfigurasi build melalui modul asli:

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

atau di 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]
  }

Anda dapat membaca konfigurasi build secara sinkron dan memutuskan dalam Javascript bagaimana Anda akan berperilaku.


0

Dimungkinkan untuk mengakses variabel dengan process.env.blablabukan process.env['blabla']. Saya baru-baru ini membuatnya bekerja dan berkomentar tentang bagaimana saya melakukannya pada masalah di GitHub karena saya punya beberapa masalah dengan cache berdasarkan jawaban yang diterima. Inilah masalahnya.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.