问题
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:
- filename.ios.[customExtension].js \ filename.android.[customExtension].js
- filename.[customExtension].js
- 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),欢迎转载,但是必须保留本文的署名和链接。