如何在 Metro 中获取 React Native 变体?

Posted

技术标签:

【中文标题】如何在 Metro 中获取 React Native 变体?【英文标题】:How to get React Native variant in Metro? 【发布时间】:2020-01-28 15:21:43 【问题描述】:

我正在开发 React Native 应用程序,该应用程序在 src/config/config.js 之类的文件中包含针对不同可能客户端的不同配置。这些配置相当复杂。该文件的结构基于客户端名称作为键,值作为对象条目,例如:

export default 
  fooClient: 
    apiUrl: "https://foo.example.com/",
  barClient: 
    apiUrl: "https://bar.example.com/"
  

当然,还有很多其他的选项键。

在构建应用程序时,通过指定 android 构建变体,我知道要为哪个客户端执行此操作,例如:

ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo

出于安全原因,我不希望其他客户端的密钥包含在配置文件中。在构建应用并将其发送给客户端之前,我有哪些选项可以从该文件中删除所有其他客户端配置

我想到了以下几点:我修改了打包程序,以便它去除与当前构建变体不对应的键。

我现在有一个用于 Metro 的转换器插件,它执行以下操作:

const upstreamTransformer = require('metro-react-native-babel-transformer');

module.exports.transform = function(src, filename, options) 
  if (typeof src === 'object') 
    // handle RN >= 0.46
    ( src, filename, options  = src);
  

  if (filename.endsWith('config.js')) 
    console.log('Transforming ' + filename);
    let srcStripped = src.replace(';', '').replace('export default ', '');
    let configObj = JSON.parse(srcStripped);
    // TODO: get the build variant and strip all keys that we do not need from configObj
    return upstreamTransformer.transform(
      src: 'export default ' + JSON.stringify(configObj) + ';',
      filename: filename,
      options
    );
   else 
    return upstreamTransformer.transform( src, filename, options );
  
;

但是我怎么知道正在使用哪个构建变体?

如果这看起来像是一个 XY 问题,我很乐意探索动态构建配置的替代方法。但是,我不能使用环境变量,因为配置太复杂而不能仅是 .env 键的列表。

【问题讨论】:

【参考方案1】:

您不应该以这种方式使用 Metro transform。它不干净,迟早可能会导致配置丢失和/或语法损坏。

我所做的并建议你,是在src/config/下创建3个不同的配置文件;一个文件用于fooClient.js,一个文件用于barClient.js,最后一个文件具有通用配置client.js。所有文件都将导出默认配置对象,但在每个fooClientbarClient 中,您将使用deepmerge 模块来合并client.js 配置:

client.js:

export default 
  commonSettingA: "...",
  commonSettings: 
    ...
  
  ...

fooClient.js:

import merge from 'deepmerge';
import config from './config';

export default merge.all([
  config,
  
    apiUrl: "https://foo.example.com/",
  
]);

barClient.js:

import merge from 'deepmerge';
import config from './config';

export default merge.all([
  config,
  
    apiUrl: "https://bar.example.com/",
  
]);

然后您可以使用环境变量来传递所需的配置并创建适当的 Metro 解析; @react-native-community/cli 不会将命令行参数传递给 Metro 配置脚本。可以使用process.argv自己解析,但是不值得。

以下是使用环境变量在metro.config.js 中创建解析的方法:

const path = require("path");

module.exports = 
  projectRoot: path.resolve(__dirname),

  resolver: 
    sourceExts: ['js', 'jsx', 'ts', 'tsx'],
    extraNodeModules: 
      // Local aliases
      "@config": path.resolve(__dirname, "src/config", `$process.env.METRO_VARIANTClient.js`)
    
  
;

使用此解析,您必须像这样导入配置:

import config from '@config';

然后添加 2 个package.json 脚本,一个用于fooClient,一个用于barClient


   ...
  "scripts": 
    "run:android:foo": "METRO_VARIANT=foo ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo",
    "run:android:bar": "METRO_VARIANT=bar ENVFILE=.env npx react-native run-android --variant barDebug --appIdSuffix bar",
    ...
  
  ...

然后你只需运行所需的脚本:

yarn run:android:foo # will build with fooClient.js
yarn run:android:bar # will build with barClient.js

【讨论】:

我回到这个答案,因为我无法理解在调用run-androidMETRO_VARIANT 变量实际上是如何传递到 Metro 服务器的。 Metro 服务器此时应该已经在运行,由npx react-native start 启动,对吧?那么它如何从另一个 shell 的 run-android 调用中获取 env,并据此做出决定呢? 如果您手动启动 Metro 服务器,那么您还必须将环境变量设置为 npx react-native start 脚本。如果您让METRO_VARIANT 自动启动地铁,这是否无法通过? 你是对的。还有另一个错误掩盖了未拾取环境的事实。再次感谢! MacOS 上通过的 ENV 在 metro.config.js 中未定义,但在任何其他应用程序文件中未定义是否有原因?在 Windows 上捆绑时,ENV 会正确传递。我尝试按照您的方式传递它们/使用cross-env 包/使用react-native-dotenv 包。问题依然存在。【参考方案2】:

你不能在命令中添加一个“重复”的参数吗?

像这样 METRO_VARIANT=fooDebug ENVFILE=.env npx react-native run-android --variant fooDebug --appIdSuffix foo

通过process.env.METRO_VARIANT获取它

您可能不想让您的设置更加复杂。根据不同客户端的数量,您可能希望每个客户端使用多个模式/变体,但这可能有点过头了。

【讨论】:

我想这是一个快速而肮脏的解决方法,谢谢。我希望了解更多有关如何从 RN 调用 Metro 的信息,以及是否可以通过某种方式传递或访问该变体。

以上是关于如何在 Metro 中获取 React Native 变体?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 React-Native-Camera 捕获获取元数据?

在 react-native 中迭代 JSON 数组

如何在 Metro (UWP) 中获取 BitmapImage 的宽度和高度?

在 react-native 项目中找不到 metro-config node_modules

React-Native Metro 捆绑器无法提供任何文件

如何在本机反应中呈现html表格?