原本准备写一篇完整的文章介绍下我司的 React Native 总体方案的实践,可是下笔以后发现话题实在太多太长,很难写成一篇观点集中的文章,因此计划分几篇文章来介绍,但愿对正在或者准备在 RN 上搞事情的同窗有所启发,另一定要根据本身的业务场景来考虑问题,切勿人云亦云,不知因此。因此本文除了介绍咱们最终是怎样作的以外,我会着重强调“为何这样作”。javascript
本文主要讲的是 RN 版本依赖和热更新相关的话题,这是咱们团队的 RN 方案和业界流行的方案差别最大的地方,也是咱们耗费心力最多的部分。本文会先讲一下方案的主要思想,而后介绍下场景和原因,最后详解一些细节的实现。前端
下文以 SRN 表明咱们团队的 React Native 方案(souche react native)。java
首先介绍核心思想,在咱们团队内部,最开始对 React Native 的版本依赖就达成这样一个共识:react
其实咱们整个方案都是围绕这两个重点来实现的,不少同窗看到这两个核心点可能会很是困惑,可能就此结束阅读此文,可是我是但愿你们可以继续看下去,看一下为何咱们要这样作再作决定。npm
要讨论这个话题,其实首先须要讨论一个终极问题:为何要引入 RN?不一样的团队可能会给出不一样的答案,这都无可厚非,毕竟每一个团队的问题和场景都不一样,天然引入一个新技术的理由也不一样,惟一最差的理由可能就是“没有理由”。json
对于咱们团队来讲,引入 RN 并非由于它的代码能够热更新这个特性,H5代码自带远程属性,为何不用H5而是要用RN?咱们的场景,偏B端,重交互而轻营销,产品关注的主要是稳定性,而不是随时变化的能力。对于须要灵活变化的场景,用H5彻底能够知足需求,交互和体验/开发效率都没有太大问题。因此我后来一直跟你们强调,RN 毫不是用来替换 H5 的。那为何要引入 RN 呢,其实在咱们正式引入 RN 以前,咱们已经在 RN 上积累了半年多,甚至组件库都作出来了,可是历来没有在业务中尝试。后来,有一个契机,公司在C轮以后走向了团队规模极速扩大的道路,以前咱们公司不少B端产品都是重客户端的,这时候团队的瓶颈开始凸显,业务的客户端开发成本很高,Native的一些缺点开始暴露,例如 开发不够灵活/跨端技术栈发展不统一/人力成本double(这是最突出的问题),对于须要快速试错快速迭代的公司阶段来讲,RN 的优点凸显出来了,而这,就是咱们引入 RN 的最重要的缘由。微信
想清楚了为何要引入 RN 以后,应该如何使用 RN 也就很清楚了,咱们并不特别关注 RN 的热更新能力,而最关注的主要是两点:app
1、如何将 RN 的开发工程化,无论是客户端开发仍是前端开发,均可以用这套方案快速开发普通的业务。框架
2、稳健性。包含多个方面,底层的稳定,代码健壮性,版本依赖管理。maven
第一点,咱们以后再讲,这是咱们整套体系中的一部分,作了不少事情来保证业务能够被快速生产,包括脚手架和开发框架/组件库等。而第二点,就是文章开头咱们提到的核心思想。
我先解释下,什么叫作强依赖。一句话表达就是:特定版本的app中某个 RN 业务的bundle的版本也会被锁定在某个版本,区别于覆盖式热更新和H5的实时访问机制。
为何须要这么作?有如下几个因素考虑:
不知道你们在使用覆盖式热更新的时候有没有遇到这些问题,但愿能有所启发。
强依赖是个好东西,可是热更新也是个好东西,咱们不能舍本逐末,咱们一直在弱化热更新的优点,可是不可否认它在某些场景下的必要性,例如紧急修复线上bug,例如一些偏营销的业务场景,也会经过热更新带来快速发布的能力。
可是,咱们在热更新和强依赖之间作出了一个平衡,将两者结合在一块儿,业务方能够将某个版本的app依赖的某个RN业务锁定在某个版本,可是这个版本同时也具有热更新能力,可能不少同窗已经猜到了,咱们是用两个版本号来维护这个逻辑的,准确来讲并非两个版本号,而是一个业界统一的版本格式:语义化版本控制规范(SemVer)。
格式如 ${major}.${feature}.${patch},遵循 semver 规范的版本号
选择须要递增的版本号
major: 主版本号,用于断代更新或大版本发布
feature: 特性版本号,用于向下兼容的特性新增
patch: 修订版本号,用于 bug 修复
递增位的右侧位须要清零,如 1.1.2 => 1.2.0复制代码
接下来咱们就讲讲这里的依赖逻辑。
接着上面语义化版本号来说咱们的具体实现,能够发现咱们须要版本锁定和热更新的需求,其实和SemVer的诉求彻底一致,天然而然的,咱们用 ${feature} 来锁定版本,用 ${patch} 来热更新,从语义上来讲,锁定版本后须要更新功能须要升级 ${feature} 版本,这就是一个 feature,须要热更新就是修复问题,升级 ${patch} ,这就是一个 hotfix(or patch)。
最终,其实就是咱们的app在用户手机上运行时,会发起一个热更新的请求,而这个热更新的具体逻辑只会判断“相同 ${feature} 下的最新的 ${patch} 版本号”,本文整篇的精华就是这句话,你们细细体会下。
具体到 SRN 中,其实这个逻辑不是放在客户端,也不是放在RN代码中。而是放在一个专门维护版本依赖关系的Node服务中,将整个过程解耦,这样依赖逻辑能够随时统一更换。
每一个RN业务发布的时候都会讲包先上传到 CDN,而后将此次发布的信息记录到这个 Node 服务中(若是不当心发错了版本,在Node服务中操做一下就能够删掉一次发布),而后每一个用户app启动的时候,会把用户本地的全部RN业务包和他们的版本发送给Node服务,Node服务会在服务端判断 咱们刚才提到的这个逻辑,返回“相同 ${feature} 下的最新的 ${patch} 版本号”,而后客户端就会启动热更新逻辑去更新Node服务返回的最新版本的bundle。
这里还有一点须要提到,其实咱们对 ${major}.${feature}.${patch} 还作了扩展,以便支持测试/开发/预发 环境上的版本,例如测试环境上的版本号会是这样:0.1.2-beta.8,也就是发布测试版本不会增长 ${patch} 版本,而是会在扩展字段上增长 ${beta} 版本,对于扩展字段,由于是开发环境,处理规则是和 ${patch} 版本同样,直接热更新,因此除了线上环境,全部其余环境更改代码都不须要从新定义依赖。
另一个问题就是这么复杂的版本规则,如何友好的暴露给开发者?其实对于开发者来讲,根本不须要本身维护版本号,也不须要知道这个规则,在业务发布前,脚手架会给选择,“环境:测试、预发、线上”,“发布类型:功能迭代、bug修复”,这些都是交互式命令,选择对应命令后,会自动升级当前业务的版本号,自动发布和打tag,无需开发手动维护。
用逻辑图表示:
关于版本依赖锁定和热更新其实到这里基本结束了的,可是仔细深刻思考的同窗应该发现好像少了点什么,的确,有这些还不够,你的整个流程还跑不起来,少了最关键的一步:依赖声明和本地集成。
特别是第一次发版的时候,客户端如何知道当前版本的app依赖了哪些业务和锁定这个业务的哪一个 ${feature} 版本呢?
在开始的时候,咱们的方案是在app的工程里维护一个package.json 同样的文件,内部声明好 dependence,和 npm 包的 package.json 格式相似。而后在jenkins或者ide里打包app的时候,会执行一段脚本,这个脚本会将这个package.json的内容上传到刚才提到的 Node 服务中,服务会走一个跟线上热更新一样的逻辑,将当前锁定的${feature}版本的最新${patch}版本返回,而后这个脚本会去下载这个最新的RN bundle,将其直接集成到工程代码中,而且更新 package.json 内的依赖声明。
当发布了一个线上bundle的时候,若是选择了“功能迭代”,会自动给业务的 ${feature} 加一,这时候须要发布新版本的客户端,就要手动去改一下 package.json 中的这个 bundle 依赖的版本号,例如业务从 1.2.3 到 1.3.0,就把package.json中的版本号改成 1.3.0 ,若是后面你发现有bug又发布了一次,不须要再改 1.3.1 了,打包的时候运行的脚本会自动帮你改掉。也就是每次发版后,这个版的app内都会默认跟一个业务的最新版的bundle。
这样作看起来挺完美的,可是后来咱们发现仍是有很多问题的,例如,咱们的脚手架没有把这个版本号的规则太多的暴露给开发者,可是开发者却须要在每次发版前维护客户端中的这个依赖,咱们的app以及业务发版都很频繁,形成整个流程不畅。另外打包的脚本稳定性和运行性能都会影响打包的过程,这个侵入对客户端和测试同窗体验很差。
如今,咱们立刻就会上一个新的方案,原理是将全部RN的包从概念上包装成 Native的包,例如iOS的pod包,Android的maven包。每次发布的时候,除了以前的流程,如今最后一步,咱们会把js bundle分别包装成一个 iOS 的 pod包,Android 的 maven 包。而后客户端直接将 RN 的业务当作一个正常的Native业务来使用。
这样,去掉了两个动做:app打包时不须要执行集成脚本了,对 app 打包流程没有任何侵入;另外,不须要在客户端声明额外的依赖了,客户端只须要用传统的方式,在 podfile 或者 gradle 里声明依赖就能够,pod包和maven包的版本和RN业务的版本彻底一致,规则也一致。
固然,这里省略了不少细节,若是你们有须要详细了解的,能够私下联系我。
由于有版本依赖锁定,最近咱们的 SRN 整个升级到 0.43 以及 集成 bundle 拆分的功能 还有整个集成过程的完全改变,都很是轻松,升级过程彻底无痛,若是你们有遇到一样的困惑,能够参考一下。后续其实咱们也打算在某些偏营销的场景中,能够不经过发版集成直接在一个app中访问一个RN业务,其实在现有的机制上作一点小改动便可,仍是那句话,看场景,不要完全否认某些方案,可能只是你们遇到的场景不一样,而技术的价值正式解决业务场景中的问题,而不是为了技术而技术。
有问题欢迎加我微信:mier963 咨询,感谢阅读。