【问题标题】:Setting environment variable in react-native?在 react-native 中设置环境变量?
【发布时间】:2016-01-12 01:31:06
【问题描述】:

我正在使用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);

标签: react-native environment-variables


【解决方案1】:

我建议使用twelve factor 建议让您的构建过程定义您的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(或 start、run-ios 或 run-android)来转换您的 react-native 代码,那么您所要做的就是让您的代码看起来像这样:

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

然后 Babel 会将其替换为:

const apiKey = 'my-app-id';

【讨论】:

  • 听起来不错的解决方案,但在 RN@0.37.0 对我不起作用。 process.env 上的唯一属性是 NODE_ENV
  • 见 Jack Zheng 下面的回答...你不能通过process.env.API_KEY访问变量...改用process.env['API_KEY']
  • 我将 process.env['API_KEY'] 设置为未定义。谁能帮我设置一下
  • 我遇到了同样的问题:未定义
  • 在 v0.56 中为我工作。每次更改环境变量时,您都必须通过运行 react-native start --reset-cache 来清除捆绑程序的缓存。
【解决方案2】:

我发现的最简单(不是最佳理想)解决方案是使用react-native-dotenv。您只需将“react-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"

【讨论】:

  • @Slavo Vojacek 如何使用它为stagingproduction 配置一个base_url
  • @CompaqLE2202x 我不太确定我理解了吗?您是在询问使用不同的 .env 文件(每个环境),还是在不同的 .env 文件中重用您的一些值,因此您不要在暂存和生产之间重复它们?
  • @SlavoVojacek 我问的是每个环境不同的.env 文件,比如说stagingproduction
  • @SlavoVojacek 你不能在 CI 阶段或部署时覆盖值吗?
  • 由于包中的最新更改,请更新 ypur 答案:“将导入从 'react-native-dotenv' 重命名为 '@env'。”否则会报错“module fs is not found”。请参阅this issuemigration guide
【解决方案3】:

在我看来,最好的选择是使用react-native-config。 它支持12 factor

我发现这个包非常有用。您可以设置多个环境,例如开发、分期、生产。

对于 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

【讨论】:

  • 可能值得注意的是,在自述文件中它指出 请记住,此模块不会混淆或加密打包的秘密,因此不要将敏感密钥存储在 .env 中。防止用户对移动应用程序的秘密进行逆向工程基本上是不可能的,所以在设计您的应用程序(和 API)时要牢记这一点
  • 问题是它不适用于像 twitter 这样的框架,它需要在你的 .env 中将它们的键设置为 com.twitter.sdk.android.CONSUMER_KEY
  • 如果您的意思是把密钥放在 Manifest 中,扩展支持它。这个答案中没有描述。您可以在 XML、Java 和 JS 文件中使用变量。
  • react-native-config 不适用于 RN 0.56,它有未解决的问题,并且超过 6 个月没有维护。女巫杀死它在 RN 中的使用的问题是 github.com/luggit/react-native-config/issues/267,这里有一些 hacking 让它工作 github.com/luggit/react-native-config/issues/285
  • xcode 如何知道您何时要推出阶段/产品版本?
【解决方案4】:

React Native 没有全局变量的概念。 它严格执行modular scope,以促进组件的模块化和可重用性。

不过,有时您需要组件了解它们的环境。在这种情况下,定义一个 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

这会创建一个singleton 环境,您可以从应用范围内的任何位置访问该环境。您必须从使用环境变量的任何组件中显式地require(...) 模块,但这是一件好事。

【讨论】:

  • 我的问题是如何getPlatform()。我已经制作了这样的文件,但无法在 React Native 中完成这里的逻辑
  • @DamonYuan 这完全取决于你如何设置你的包。我什至不知道stagingproduction 是什么意思,因为它取决于您的环境。例如,如果您想要 IOS 和 Android 的不同风格,那么您可以通过导入您的 index.ios.jsindex.android.js 文件并在那里设置平台来初始化环境,例如Environment.initialize('android').
  • @DamonYuan 做了我提供的帮助,还是您需要更多说明?
  • 当您可以控制代码时,这非常好。我正在运行依赖于 process.env 的第三方模块,所以...
  • 如果您创建一个env.js 文件,请务必在签入到存储库时忽略它,并将使用的密钥(带有空字符串值)复制到另一个您签入的env.js.example 文件中因此其他人可以更轻松地构建您的应用程序。如果您不小心签入了项目机密,请考虑 rewriting history 不仅将其从源中删除,还从其历史记录中删除。
【解决方案5】:

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

例如:

//vars.js

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

export {url, publicKey}

那么只需import {url} from '../vars',您总会得到正确的。不幸的是,如果您想要两个以上的环境,这将不起作用,但它很容易并且不涉及向您的项目添加更多依赖项。

【讨论】:

  • 即使在 xcode 中创建发布版本,您是否知道将 DEV 设置为 TRUE 的方法?
  • 不。当我想使用开发变量进行发布构建时,我只是注释掉产品变量,然后将开发变量复制粘贴到产品部分。
  • 我发现这是最优雅的解决方案
  • 不是一个糟糕的解决方案,但也不是很好,因为它只处理布尔行为。也就是说,无论是否开发。我将如何处理超过 2 个环境?您也可以使用process.env.NODE_ENV,因为它提供developmentproduction。大多数人需要使用 dev、qa、staging、prod 等来提升应用程序。
【解决方案6】:

我已经为相同的问题创建了一个预构建脚本,因为我需要一些不同的 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 run..

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

【讨论】:

    【解决方案7】:

    用于设置环境变量的具体方法因 CI 服务、构建方法、平台和您使用的工具而异。

    如果您使用 Buddybuild for CI 来构建应用程序和 manage environment variables,并且您需要从 JS 访问配置,请创建一个带有键(具有空字符串值)的 env.js.example,用于签入源代码管理,并使用 Buddybuild 在构建时在 post-clone 步骤中生成一个 env.js 文件,从构建日志中隐藏文件内容,如下所示:

    #!/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 variables (例如BUDDYBUILD_VARIANTS)管理文件的写入方式,以便更好地控制构建时配置的生成方式。

    【讨论】:

    • 总的来说我喜欢这个想法,但是env.js.example 部分是如何工作的?假设我想在我的本地环境中启动该应用程序。如果我的 env.js 文件在 gitignore 中,并且 env.js.example 用作大纲,则 env.js.example 不是合法的 JS 扩展,所以我对你这部分的意思有点困惑
    • @volk env.js.example 文件作为参考文档位于代码库中,是关于应用程序想要使用哪些配置键的规范事实来源。它既描述了运行应用程序所需的密钥,也描述了复制和重命名后预期的文件名。该模式在使用 dotenv gem 的 Ruby 应用程序中很常见,这是我从中提取该模式的地方。
    【解决方案8】:

    我使用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'] 而不是 process.env.STRING,否则将无法正确转换。

    【讨论】:

    • REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly. 谢谢!这是让我绊倒的那个!!!
    【解决方案9】:

    我认为类似于以下库的东西可以帮助您解决难题中缺少的部分,即 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

    【讨论】:

    • 赞成提供一种未提及的替代方法。没有一种尺寸适合所有人。
    • 异步拉取请求已被合并
    • react-native-env 似乎不支持 Android。有什么意义?
    【解决方案10】:

    第 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
    

    【讨论】:

      【解决方案11】:

      我使用react-native-config 为我的项目设置了多个环境。自述文件非常清楚地解释了如何在项目中配置库。只需确保实现 Android 的额外步骤部分即可。

      此外,在设置多个环境时,请确保根据您的系统终端在 package.json 中指定正确的启动命令。我在 Windows 笔记本电脑中开发了 Android 代码,在 Macbook 中开发了 iOS 代码,所以我在 package.json 中各自的启动命令是 -

      "scripts": {
              "android:dev": "SET ENVFILE=.env.dev && react-native run-android",
              "android:prod": "SET ENVFILE=.env.prod && react-native run-android",
              "ios:dev": "ENVFILE=.env.dev react-native run-ios",
              "ios:prod": "ENVFILE=.env.prod react-native run-ios",
      },
      

      如果您只需要维护一个 .env 文件,请考虑使用 react-native-dotenv 作为一个更轻的选择,虽然我在为这个库设置多个 .env 文件时确实遇到了一些问题。

      【讨论】:

        【解决方案12】:

        根据文档https://docs.expo.io/guides/environment-variables/,如果您使用 Expo,有两种方法可以做到这一点

        方法 #1 - 在应用清单 (app.json) 中使用 .extra 属性:

        在您的app.json 文件中

        {
          expo: {
            "slug": "my-app",
            "name": "My App",
            "version": "0.10.0",
            "extra": {
              "myVariable": "foo"
            }
          }
        }
        

        然后要访问代码(即 App.js)中的数据,只需导入 expo-constants

        import Constants from 'expo-constants';
        
        export const Sample = (props) => (
          <View>
            <Text>{Constants.manifest.extra.myVariable}</Text>
          </View>
        );
        

        此选项是一个很好的内置选项,不需要安装任何其他软件包。

        方法#2 - 使用 Babel 来“替换”变量。这是您可能需要的方法,尤其是在您使用裸工作流时。其他答案已经提到如何使用babel-plugin-transform-inline-environment-variables 实现它,但我将在此处留下官方文档的链接以了解如何实现它:https://docs.expo.io/guides/environment-variables/#using-babel-to-replace-variables

        【讨论】:

          【解决方案13】:

          对于最新的 RN 版本,您可以使用这个原生模块:https://github.com/luggit/react-native-config

          【讨论】:

            【解决方案14】:

            你好,如果你遇到这个问题,试试这个,这对我有用,稍后谢谢我

            在 bable.js 中

             plugins: [
                  [
                    "module:react-native-dotenv",
                    {
                      moduleName: "react-native-dotenv",
                    },
                  ],
                ],
            

            使用

            import { YOURAPIKEY } from "react-native-dotenv";
            
            
            inseted  of
            
            import { YOURAPIKEY } from "@env";
            

            【讨论】:

            • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
            【解决方案15】:

            你也可以有不同的环境脚本: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 未提及的内容?
            【解决方案16】:

            @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 中决定您的行为方式。

            【讨论】:

              【解决方案17】:

              可以使用process.env.blabla 而不是process.env['blabla'] 访问变量。我最近让它工作并评论了我是如何在 GitHub 上的一个问题上做到这一点的,因为根据接受的答案,我在缓存方面遇到了一些问题。 Here 是问题所在。

              【讨论】:

                【解决方案18】:

                [Source] 根据我的发现,默认情况下,只能进行生产和开发配置(没有暂存或其他环境)——对吗?

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

                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;
                

                替代品

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

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

                【讨论】:

                • 您可以定义任意数量的发布通道名称,并测试名称以定义您的环境变量。我看到的限制是在未定义 releaseChannel 的开发环境中。所以也许你可以使用 babel-plugin-transform-inline-environment-variables - 你可以在你的脚本中传递环境变量并在你的 environment.js 文件中引用 process.env['VAR_NAME'] if dev?
                【解决方案19】:

                不要传递这些变量,如 VAR=value react-native run-androidVAR=value react-native run-ios . 这些变量只有在我们将它们传递到start 命令时才能访问,即 VAR=value react-native start --reset-cache

                您可以通过 3 个简单的步骤来实现这一点:-

                1. 通过运行npm i babel-plugin-transform-inline-environment-variables --save-dev 安装babel-plugin-transform-inline-environment-variables

                2. "plugins": [ "transform-inline-environment-variables" ] 添加到您的.bablercbabel.config.js

                3. 在启动 Metro bundler 时传递变量,即 VAR=value reacti-native start --reset-cache不要在 react-native run-androidreact-native run-ios 命令中传递这些变量。

                请记住,使用 --reset-cache 标志是必需的,否则将不会应用变量更改。

                【讨论】:

                  【解决方案20】:

                  经过长期努力,我意识到 react-native 并没有正式提供这个功能。而且这是在babel-ecosystem中,所以我应该学习如何编写babel插件......

                  /**
                   * A simple replace text plugin in babel, such as `webpack.DefinePlugin`
                   * 
                   * Docs: https://github.com/jamiebuilds/babel-handbook
                   */
                  function definePlugin({ types: t }) {
                      const regExclude = /node_modules/;
                      return {
                          visitor: {
                              Identifier(path, state) {
                                  const { node, parent, scope } = path;
                                  const { filename, opts } = state;
                                  const key = node.name;
                                  const value = opts[key];
                  
                                  if (key === 'constructor' || value === undefined) { // don't replace
                                      return;
                                  }
                                  if (t.isMemberExpression(parent)) { // not {"__DEV__":name}
                                      return;
                                  }
                                  if (t.isObjectProperty(parent) && parent.value !== node) { // error
                                      return;
                                  }
                                  if (scope.getBinding(key)) { // should in global
                                      return;
                                  }
                                  if (regExclude.test(filename)) { // exclude node_modules
                                      return;
                                  }
                                  switch (typeof value) {
                                      case 'boolean':
                                          path.replaceWith(t.booleanLiteral(value));
                                          break;
                                      case 'string':
                                          path.replaceWith(t.stringLiteral(value));
                                          break;
                                      default:
                                          console.warn('definePlugin only support string/boolean, so `%s` will not be replaced', key);
                                          break;
                                  }
                              },
                          },
                      };
                  }
                  
                  module.exports = definePlugin;
                  

                  就是这样,然后你可以这样使用:

                  module.exports = {
                      presets: [],
                      plugins: [
                          [require('./definePlugin.js'), {
                              // your environments...
                              __DEV__: true,
                              __URL__: 'https://example.org',
                          }],
                      ],
                  };
                  

                  回答者提到的包也很棒,我也咨询metro-transform-plugins/src/inline-plugin.js

                  【讨论】:

                    猜你喜欢
                    • 2022-01-01
                    • 2022-12-19
                    • 2020-02-04
                    • 1970-01-01
                    • 2019-03-24
                    • 2017-03-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-07-13
                    相关资源
                    最近更新 更多