如何根据RN ABTest配置,构建多个不同的Bundle

August 08, 2020

问题

ABTest中,对同一个组件的实现,总是会有多种不同的方案,然后根据用户的类型来选择使用哪种方案。但是如果把AB两种方案的都打包进来,一方面会增加jsbundle的大小,另一方面,实现上也不太优雅。

所以想要找到一种方式,能够在ABTest中分别构建A、B两种包,下发给用户。每种方案中只包含自己需要依赖的代码,而不包含另一个方案需要的代码。

官方方式

在RN里面,说到区分类型来执行代码,其实是已经有的。官方文档里,RN有两种方式来区分平台构建不同的代码。一种是使用Platform组件,另一种是平台后缀名

Platform组件是代码级别的,比如:

const Component = Platform.select({
  native: () => require('ComponentForNative'),
  default: () => require('ComponentForWeb')
})();

<Component />;

平台后缀名是文件级别的,比如:

Container.js # 用来webpack执行,或者其他web构建器
Container.native.js # 用来给React Native构建器metro使用,构建安卓和iOS
Component.ios.js # 只有ios会使用
Component.android.js # 只有安卓会使用

两种方式对比来看,后缀名方式其实比较好理解,而且可以让构建依赖更加明显,不需要的依赖可以直接不构建到bundle里。所以就开始寻找如何能够实现比如component.a.js和component.b.js这样的方式来区分AB两个方案。

实现思路

第一种:

从metro的配置文件下手,寻找是否可以自定义一个文件resolver,这样就可以自己来判断加载哪个包了,增加自己的逻辑。详细阅读了配置文档之后,发现确实有个resolveRequest,好像是可以自定义一个CustomResolver。

第二种:

除了CustomResolver,其实直接修改metro的代码也是可以的。参考native.js/ios.js/andorid.js的后缀方式来实现,应该也可以做。但是要修改源码,会复杂一些。而且如果以后metro升级了,也可能会存在适配成本。

第三种:

偶然看到一个github库:react-native-repackager,在readme里介绍使用方式,就是用后缀名来区分文件以mock端对端测试。

First add your custom extension (mock) file to your project, for example:

The packager will search for custom files in the following order:

  1. filename.ios.[customExtension].js \ filename.android.[customExtension].js
  2. filename.[customExtension].js
  3. filename.ios.js \ filename.android.js

看上去非常满足需求呀,不用自己写代码来做了。但是这个库也提到“This project is no longer needed as of react-native@0.55.x”。莫非0.55.x之后官方就已经支持类似的方式了。

参考这个项目里的配置方式:

module.exports = {
 getSourceExts: () => process.env.RN_FLAVOR === 'E2E' ? ['e2e.js'] : []
}

这其实是在配置文件的后缀名,当是E2E模式的时候,增加一个后缀名e2e.js

而Metro的配置本身就支持后缀名配置。于是我们在项目中增加一个后缀名尝试一下。

实现

根据设计,ABTest组件的目录如下:

componentOne
├── index.js # 默认方式 
├── index.strategyA.js  #策略A
└── index.strategyB.js  #策略B

引用方是不关心组件内部是怎么选择的,所以引用方式如下:

import ComponentOne from './_abcomponents/componentOne';

如果选择了策略A,那么在metro的配置文件里增加strategyA.js为后缀。

   getSourceExts() {
        return ['strategyA.js', 'js', 'jsx', 'less'];
    }

经过测试发现,确实是可以满足需求的。只需要的配置文件里,根据当前的构建环境选择不同的后缀,就可以实现分离ABTest代码,让用户的jsbundle包更小。

总结

最后的实现方式看似简单,但是在开始看到配置文件时,并没有想到使用getSourceExts来实现,一直想要通过自定义resolver或者改写metro代码来扩展功能。绕了一大圈,最简单的实现方式竟然就在眼前。。

本文首发于 一粟(https://www.zeyio.com),欢迎转载,但是必须保留本文的署名和链接。

本文永久链接:https://www.zeyio.com/rn-abtest-build/