Javascript 如何在Metro中获得React本机变体?

Javascript 如何在Metro中获得React本机变体?,javascript,react-native,metro-bundler,Javascript,React Native,Metro Bundler,我正在开发React本机应用程序,该应用程序包括针对不同可能客户端的不同配置,位于一个文件中,如src/config/config.js。这些配置相当复杂。该文件的结构基于客户端名称作为键,以及作为对象项的值,例如: 导出默认值{ 客户:{ apiUrl:“https://foo.example.com/", 酒吧客户:{ apiUrl:“https://bar.example.com/" } } 当然,还有许多其他选项键 在构建应用程序时,我通过指定Android构建变体,知道要为哪个客户端

我正在开发React本机应用程序,该应用程序包括针对不同可能客户端的不同配置,位于一个文件中,如
src/config/config.js
。这些配置相当复杂。该文件的结构基于客户端名称作为键,以及作为对象项的值,例如:

导出默认值{
客户:{
apiUrl:“https://foo.example.com/",
酒吧客户:{
apiUrl:“https://bar.example.com/"
}
}
当然,还有许多其他选项键

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

ENVFILE=.env npx react本机运行android--variant fooDebug--appIdSuffix foo
出于安全原因,我不希望其他客户端的密钥包含在配置文件中。在生成应用程序并将其发送给客户端之前,我有哪些选项可以从该文件中删除所有其他客户端配置

我考虑了以下几点:我修改了打包程序,以便它去掉与当前构建变量不对应的键

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

const upstreamTransformer=require('metro-react-native-babel-transformer');
module.exports.transform=函数(src、文件名、选项){
如果(类型src==='object'){
//手柄RN>=0.46
({src,filename,options}=src);
}
if(filename.endsWith('config.js')){
log('Transforming'+文件名);
让srcStripped=src.replace(';','').replace('export default','');
让configObj=JSON.parse(srcStripped);
//TODO:获取构建变量并从configObj中删除我们不需要的所有密钥
返回上游变压器.transform({
src:'export default'+JSON.stringify(configObj)+';',
filename:filename,
选择权
});
}否则{
返回upstreamTransformer.transform({src,filename,options});
}
};
但是我如何知道正在使用哪个构建变体呢

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

您不能只向命令添加一个“复制”参数吗

像这样
METRO\u VARIANT=fooDebug ENVFILE=.env npx react native run android--VARIANT fooDebug--appIdSuffix foo

并通过
process.env.METRO\u VARIANT


您可能不想让您的设置变得更复杂。根据不同客户端的数量,您可能希望每个客户端使用多个架构/变体,但这可能太过分了。

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

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

client.js
导出默认值{
commonSettingA:“…”,
共同设定:{
...
}
...
}
fooClient.js
从“deepmerge”导入合并;
从“/config”导入配置;
导出默认合并.all([
配置,
{
apiUrl:“https://foo.example.com/",
}
]);
barClient.js
从“deepmerge”导入合并;
从“/config”导入配置;
导出默认合并.all([
配置,
{
apiUrl:“https://bar.example.com/",
}
]);
然后,您可以使用环境变量传递所需的配置并创建适当的metro resolve;不将命令行参数传递给metro config脚本。您可以使用
process.argv
自行解析它,但这不值得

以下是如何使用环境变量在
metro.config.js
内部创建解析:

const path=require(“路径”);
module.exports={
projectRoot:path.resolve(_dirname),
解析程序:{
sourceExts:['js','jsx','ts','tsx'],
节点外模块:{
//本地别名
“@config”:path.resolve(uu dirname,“src/config”,“${process.env.METRO\u VARIANT}Client.js`)
}
}
};
使用此解决方案,您必须像这样导入配置:

从'@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",
    ...
  }
  ...
}
然后您只需运行所需的脚本:

warn run:android:foo#将使用fooClient.js构建
warn run:android:bar#将使用barClient.js构建

我想这是一个快速而肮脏的解决方法,谢谢。我希望了解更多关于如何从RN调用Metro的信息,以及是否有办法传递或访问该变体。这太棒了。很快就会尝试。谢谢!@slhck谢谢。我也非常喜欢这种方式。它非常干净且有条理。如果您有任何tr,请告诉我当你试图在你的项目中实现它的时候,我会问。我回到这个答案,因为我很难理解在调用
runandroid
时,
METRO\u变量实际上是如何传递给METRO服务器的。METRO服务器应该已经在这个时间点运行了,由
npx-react-native-start启动
,对吗?那么它如何从另一个shell的
run android
调用中获取env,并据此做出决定呢?如果手动启动metro server,那么您还必须将环境变量设置为
npx react native start
脚本。是否