在react-native中设置环境变量吗?


152

我正在使用react-native来构建跨平台应用程序,但是我不知道如何设置环境变量,以便可以为不同的环境使用不同的常量。

例:

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

Answers:


138

建议不要使用十二个因素建议,让您的构建过程定义您BASE_URL和您的应用,而不是对应用程序常数进行硬编码并在环境上进行切换(我将在稍后说明)。API_KEY

为了回答如何将您的环境暴露在其中react-native,我建议使用Babel的babel-plugin-transform-inline-environment-variables

要使其正常工作,您需要下载插件,然后需要设置,.babelrc并且其外观应如下所示:

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

因此,如果您通过运行API_KEY=my-app-id react-native bundle(或启动,run-ios或run-android)来转换本机代码,那么您要做的就是使代码如下所示:

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

然后Babel将其替换为:

const apiKey = 'my-app-id';

希望这可以帮助!


7
听起来像是很好的解决方案,但在RN@0.37.0上不适合我。的唯一属性process.envNODE_ENV
亚当·法里纳

2
请参阅杰克·郑(Jack Zheng)的以下回答...您无法通过process.env.API_KEY... 访问变量,请process.env['API_KEY']改用
史蒂芬·叶(Steven Yap)

6
我正在获取process.env ['API_KEY']为未定义。谁能帮助我进行设置
user1106888

2
我遇到了同样的问题:未定义
Guto Marrara Marzagao

7
在v0.56中为我工作。您必须在react-native start --reset-cache每次更改环境变量时运行,以清除捆绑程序的缓存。
soheilpro

55

我发现最简单的(不是最佳理想的)解决方案是使用react-native-dotenv。您只需.babelrc在项目根目录的文件中添加“ react-native-dotenv”预设即可,如下所示:

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

创建一个.env文件并添加属性:

echo "SOMETHING=anything" > .env

然后在您的项目(JS)中:

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

1
我希望有一个基于.env的解决方案。谢谢!
Anshul Koka

3
@Slavo Vojacek如何使用它来base_urlstaging和配置一个production
Compaq LE2202x

@ CompaqLE2202x我不太确定我了解吗?您是要使用不同的.env文件(每个环境),还是要在不同的文件中重用某些值.env,这样您就不会在登台和生产中重复使用它们?
Slavo Vojacek

5
@SlavoVojacek我要询问的是.env每个环境的不同文件,例如stagingproduction
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

或组装用于生产的应用程序(在我的情况下为android):

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

9
可能值得注意的是,在README中它指出状态该模块不会混淆或加密用于包装的秘密,因此请勿将敏感密钥存储在.env中。基本上不可能防止用户对移动应用程序的秘密进行反向工程,因此请牢记这一点来设计您的应用程序(和API)
Marklar

问题是它不适用于某些框架,例如twitter,要求将它们的密钥设置为.env中的com.twitter.sdk.android.CONSUMER_KEY
thibaut noah

如果您打算将密钥放入清单中,则扩展支持它。只是没有在此答案中描述。您可以在XML,Java和JS文件中使用变量。
sfratini

4
react-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

my-component.js

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

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

这将创建一个单例环境,可以从应用程序范围内的任何位置对其进行访问。您必须require(...)从使用环境变量的所有组件中显式地定义模块,但这是一件好事。


19
我的问题是如何getPlatform()。我做这样的文件,但不能在这里完成逻辑阵营原住民
达蒙元

@DamonYuan完全取决于您如何设置软件包。我不知道staging或者production甚至平均,因为它依赖于您的环境。例如,如果您希望IOS和Android使用不同的风格,则可以通过导入Environment index.ios.jsindex.android.js文件并在其中设置平台来初始化Environment ,例如Environment.initialize('android')
tohster

@DamonYuan帮了我什么,还是您需要更多说明?
chapinkapa

当您控制代码时,这非常好。我正在运行依赖于process.env的第三方模块,所以...
enapupe

2
如果您创建env.js文件,请确保在签入到存储库时将其忽略,并将使用的键(带有空字符串值)复制到env.js.example您要签入的另一个文件中,以便其他人可以更轻松地构建应用。如果您不小心签入项目机密,请考虑重写历史记录,以不仅从源中删除它们,而且从历史记录中删除它们。
乔什·哈布达斯

17

我使用了__DEV__内置于react-native 的polyfill来解决此问题。true只要您不构建用于生产的本机反应,它将自动设置为。

例如:

//vars.js

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

export {url, publicKey}

然后公正import {url} from '../vars',您将永远得到正确的。不幸的是,如果您需要两个以上的环境,那么这将行不通,但是它很简单,并且不需要在项目中添加更多的依赖项。


您知道即使在xcode中创建发行版时,也可以将DEV设置为TRUE的方法吗?
realtebo '18

1
不。我只是注释掉prod vars,然后在要使用dev变量进行发行版本时将dev vars复制粘贴到prod部分中。
Logister

1
我发现这是最优雅的解决方案
Dani Sh90 '19

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 2016年

@volk该env.js.example文件作为参考文档位于代码库中,是有关应用程序要使用的配置键的一个真实的标准来源。两者都描述了运行该应用程序所需的键以及复制和重命名后预期的文件名。该模式在使用dotenv gem的 Ruby应用程序中很常见,这就是我从中提出该模式的地方。
乔什·哈布达斯

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
支持提供未提及的替代方法。没有一种尺寸能适合所有人。
乔什·哈布达斯

异步拉取请求已合并到
jcollum,2016年

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来执行 react-native运行..

我的package-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

每个环境文件:

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']NOT process.env.STRING,否则它将无法正确转换。


REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.谢谢!这是让我绊倒的那个!!!
Steven Yap'17年

1

[来源]根据我的发现,默认情况下看起来像是只能进行生产和开发配置(没有暂存或其他环境)–是吗?

现在,我一直在使用一个environment.js文件,该文件可用于检测expo发布通道并基于此更改返回的变量,但是对于构建,我需要将返回的non- DEV变量更新为staging或产品:

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;

备择方案

有没有人对使用expo构建的项目使用react-native-dotenv有经验?我很想听听你的想法

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


您可以定义任意数量的发行渠道名称,并测试名称以定义环境变量。我看到的限制是在未定义releaseChannel的开发环境中。因此,也许您可​​以使用babel-plugin-transform-inline-environment-variables-您可以在脚本中传递环境变量,并在dev.js文件中引用process.env ['VAR_NAME'](如果开发)?
colemerrick19年

0

您还可以具有不同的环境脚本:production.env.sh development.env.sh production.env.sh

然后在开始工作时将它们作为来源(仅与别名相关联),以便将所有sh文件导出到每个env变量:

export SOME_VAR=1234
export SOME_OTHER=abc

然后添加babel-plugin-transform-inline-environment-variables将允许在代码中访问它们:

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

您是否在添加@chapinkapa没说的话?
Maximo Dominguez

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上的问题上的处理方式,因为基于公认的答案,我在缓存方面遇到了一些问题。是问题。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.