ChatGPT解决这个技术问题 Extra ChatGPT

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

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

N
Nimantha

我建议您使用 twelve factor 建议,让您的构建过程定义您的 BASE_URL 和您的 { 3}。

要回答如何将您的环境暴露给 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 来清除捆绑程序的缓存。
P
Patrik Prevuznak

在我看来,最好的选择是使用 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
如果您的意思是将密钥放在清单中,则扩展程序支持它。这个答案中没有描述。您可以在 XML、Java 和 JS 文件中使用变量。
react-native-config 不适用于 RN 0.56,它有未解决的问题,并且超过 6 个月没有维护。女巫杀死它在 RN 中的使用的问题是 github.com/luggit/react-native-config/issues/267,这里有一些黑客让它工作 github.com/luggit/react-native-config/issues/285
xcode 如何知道您何时想要推出 stage / prod 版本?
s
swrobel

我发现的最简单(不是最佳理想)解决方案是使用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
t
tohster

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

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

我的组件.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,不仅要从源中删除它们,还要从其历史记录中删除它们。
L
Logister

为了解决这个问题,我使用了 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 等来提升应用程序。
T
Toni Chaz

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

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

v
vhs

用于设置环境变量的具体方法因 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_VARIANTS 之类的 Buddybuild variables 管理文件的写入方式,例如,以更好地控制构建时配置的生成方式。


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

第 1 步:像这样创建单独的组件 组件名称:pagebase.js 第 2 步:在此内部使用代码

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

第三步:在任何组件中使用它,使用它首先导入这个组件然后使用它。导入并使用它:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY

O
Olcay Ertaş

我使用 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. 谢谢!这是让我绊倒的那个!!!
l
leonfs

我认为以下库之类的东西可以帮助您解决难题中缺少的部分,即 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。重点是什么?
N
Nishant Kohli

我使用 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 文件时确实遇到了一些问题。


您如何将实际的构建时间变量读入您的项目中?即我可能有一些变量可以添加到这些文件中。但是一些密钥应该来自构建环境?
J
Jo E.

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

方法 #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


S
Smakosh

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


V
Vijay Joshua Nadar

如果您正在使用 expo(托管工作流)开发应用程序,则必须在项目的根目录中创建一个名为 app.config.js 的文件,并将以下代码添加到该文件中:

const myValue = "My App";

export default () => {
    if (process.env.MY_ENVIRONMENT === "development") {
        return {
            name: myValue,
            version: "1.0.0",
            // All values in extra will be passed to your app.
            extra: {
                fact: "dogs are cool"
            }
        };
    } else {
        return {
            name: myValue,
            version: "1.0.0",
            // All values in extra will be passed to your app.
            extra: {
                fact: "kittens are cool"
            }
        };
    }
};

然后你应该使用下面的命令启动/发布你的应用程序(这将在 Windows 中工作。对于其他操作系统,请阅读我在最后提到的文章)。

npx cross-env MY_ENVIRONMENT=开发博览会开始/发布

这将使用上面提到的环境变量 (MY_ENVIRONMENT) 启动或发布您的应用程序。应用程序将根据环境变量加载适当的配置。现在,您可以通过将名为 expo-constants 的模块导入项目文件来从配置中访问变量 extra 。例如:

import Constants from "expo-constants";

export default function App() {
    console.log(Constants.manifest.extra.fact);
    return (
        <>
            <View>
                <Text>Dummy</Text>
            </View>
        </>
    );
}

使用 Constants.manifest 我们可以访问 extra 内的对象。所以如果你的环境变量是development,这段代码应该是console.log "dogs are cool"。我希望这很有用。有关详细信息,请参阅此 article


P
Pikachu-go

你也可以有不同的环境脚本: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 没有说的任何内容?
v
vonovak

@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 中决定您将如何表现。


S
Srdjan Cosic

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


K
Kemal Ahmed

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

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

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 - 你可以在你的脚本中传递环境变量,如果 dev?
M
Muhammad Awais

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

在 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
N
Nimantha

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

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

通过运行 npm i babel-plugin-transform-inline-environment-variables --save-dev 安装 babel-plugin-transform-inline-environment-variables。将 "plugins": ["transform-inline-environment-variables" ] 添加到您的 .bablerc 或 babel.config.js 中。在启动 Metro bundler 时传递变量,即 VAR=value reacti-native start --reset-cache,不要在 react-native run-android 或 react-native run-ios 命令中传递这些变量。

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


L
LitileXueZha

经过长时间的努力,我意识到 react-native 并没有正式提供这个功能。这是在 babel 生态系统中,所以我应该学习如何编写 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