前言
本文根据5月30日京东前端架构师汤小丽在绿盟京东专场直播活动分享的《京东在ReactNative上的实践》主题演讲整理而成。专家主要从JDRN业务现状、RN官方在性能提升上的优化、JDRN基于RN的优化、RN新架构四方面为大家介绍。下面接着上篇的内容,继续介绍JDRN基于RN的优化方案以及RN新架构。
一、JDRN基于RN的优化方案
1.1拆分包-业务包二次拆分
上篇介绍了基础包的拆分,同样的原理,那业务包如何二次拆分?业务包二次拆分主要是为了提高首屏加载效率。二次拆分是把首屏内容拆出来,除了首屏以外的其他部分,可再给它拆成另外一部分。
由于篇幅有限,今天重点介绍业务包拆分为首包、二包的内容。假设业务包的基础包已经被剥离出去,业务包分为entrance.js(当前业务主入口文件)、page1.js(首包入口文件)、page2.js(二包入口文件)、common(首包、二包共同依赖的部分)。下图中第二个框是配置的入口文件,第三个框是Metro打包服务。第一步分别以首包入口文件和二包入口文件构建出2个包,构建时输出记录着包中依赖文件及对应id的2个映射表,如map1.json、map2.json,这两个映射表中都有的文件就是首包、二包的公共依赖部分map1&2.json。从二包里减去map1&2.json的公共部分,剩下的就是必须要放到二包中的文件map2-1&2.json。第二步是打出业务首包,基于业务主入口文件构建业务包,构建过程中过滤掉需要放到二包中的文件map2-1&2.json,输出map3.json文件,这个map3文件里放的就是加载首包必须要的所有文件了。第三步是打出业务二包,仍然基于业务主入口文件构建业务包,过滤掉map3.json中的文件出来的就是二包的内容了。构建业务包的二次拆分的过程虽然复杂,但在业务加载时首先只需要加载首包的内容,减少了JS引擎需要解析-编译-执行的内容,所以首屏加载性能能明显提升。二包可以在首屏加载完成后再进行懒加载。
1.2基础包预加载
启动RN引擎,先创建JavaScript运行环境,再去创建NativeModules,主要用于注册桥接模块。创建MessageQueue,是因为RN的JavaScript与原生不能直接交互,两边的数据传输、数据交互需要在消息队列中完成。注册JavaScriptModule,启动JavaScript运行环境,这里主要是创建JavaScript引擎对象、JavaScript引擎运行时。完成创建后,基于官方提供的能力,在执行虚线框时,就已经给到了业务包。加载业务包,JavaScript引擎解析和执行jsbundle,在执行的过程中加载Assets及NativeModules,加载完再去做渲染。
如果想要加载普通的业务包是需要执行完虚线框后,才能解析、执行jsbundle。基础包预加载是指使用基础包创建一个可用的RN引擎,提前做完上面虚线框中的这些工作。当需要打开一个RN业务时,直接拿之前创建好的RN引擎,在其JS上下文中执行业务拆分包,这样就节省了RN引擎初始化的这段时间。那如何加载业务二包?在业务首包加载完成后,在首包里触发二包的懒加载,实际上也是使用同一个RN引擎,在其JS上下文中执行业务二包。执行虚线框的部分在JavaScript引擎里,大概需要花费150ms,通过预加载的方式可以节省150ms。预加载基础包iOS内存约增加2M,安卓约增加10M,内存方面也是可以接受的。
1.3数据接口缓存
数据接口缓存是简单有效提升首屏加载性能的方式,只需要为业务提供简单易用的数据存取接口即可。在项目启动时开始利用宏任务异步获取缓存数据,和页面初始化逻辑并行,减少数据反序列化带来的时间损耗。
1.4业务热更新逻辑
京东APP内几乎所有的业务都是通过热更新的方式去下载。拆分包性能更好的原因是在于可以做RN引擎的预加载,节省了一部分的时间。但拆分包是不能够独立运行的,依赖于基础包。不同版本的APP里基础包的版本有可能不同,因为基础包也在不断升级,比如新增或修改接口。所以RN业务拆分包有一个依赖的基础包版本的设置,且基础包需要做到向前兼容。
业务拆分包的演进,由全包变成了多个业务的拆分包和一个基础包,基础包内置在APP里。业务热更新逻辑是业务在更新的时候,首先会去判断该业务拆分包依赖的基础包版本和APP内置的基础包版本是否匹配(小于等于)。若匹配得上,就优先去下载拆分包。因为拆分包可以保证业务快速下载,完成加载。若匹配不上,可以考虑下载业务全包,至少可以保证业务的可用性。业务加载时,如果发现APP本地有预热RN引擎,直接用预热引擎加载当前的业务拆分包。如果预热引擎还没有完成预热,通过合并基础包和拆分包的方式把业务加载起来。若本地的基础包和拆分包不匹配,可以考虑全包兜底。这就是业务热更新的流程。热更新下发的是拆分包或者全包的diff包版本,拿到diff包后在APP里做patch的合并,在实践过程中要注意patch的过程中需要多次校验,保证patch结果的正确性。
二、RN新架构
截止目前,RN的版本已经升级到0.71版本,而在2022年的0.68版中已经包含新架构代码。对于新架构的核心,总结提炼为以下几点:
(1)去除JSBridge,使用JSI模块将native的对象直接暴露给JavaScript。JavaScript可以保存对这些对象的引用,并且可以使用该引用直接调用方法。现有的调用原生模块的方式是通过桥发送序列化的json信息,这些操作是耗时且无法满足同步的场景。
(2)新渲染系统Fabric取代UI Manager。它将先在主线程或本机线程中同步执行滚动、手势等用户交互,而 API请求等其他任务将异步执行。这将极大提高交互体验。
(3)Turbo 实现本地模块的的延迟加载。在当前架构中,JavaScript使用的所有本机模块(例如蓝牙、地理位置、文件存储等)都必须在打开应用程序之前进行初始化。这意味着,即使用户不需要特定模块,它仍然必须在启动时进行初始化。
简而言之,新架构的方案将会带来巨大的性能提升、更短的应用启动时间(接近原生)、更快的开发效率。我们也会持续关注和跟进中,争取早日将新框架融入到我们的业务中。
参考文献:
https://reactnative.dev/docs/ram-bundles-inline-requires
https://medium.com/coox-tech/deep-dive-into-react-natives-new-architecture-fb67ae615ccd