看Facebook是如何优化React Native性能

原文出处: facebook   译文出处:@Siva海浪高   react

该文章翻译自Facebook官方博客,传送门git

React Native 容许咱们运用 React 和 Relay 提供的声明式的编程模型,写JavaScript来构建咱们的 iOS 和 Android 的应用。这样的作法使得咱们的代码更精简,更容易理解和阅读,这些代码还能够在多个平台共享。咱们也能够加快迭代速度(由于在开发时不用等待漫长的编译。使用React Native,咱们能够发布更快,打磨更多细节,让应用运行的更流畅。这其中优化性能是咱们工做的一大重要部分,接下来说述 Facebook 如何使应用性能足足提高两倍的故事~github

为何要加快?

当应用运行的更快,内容加载的更迅速,就意味着用户能够有更多时间来使用应用,流畅的动画让用户更加享受的使用应用。在新型市场中,2G网络和几年前的机型仍是主力。这时那些性能良好的和那些运行卡顿就有很大差异了。web

自从发布了 iOS 和 Android 版本的 React Native 后,咱们团队一直在诸如 提高列表视图的滚动性能,优化内存占有,让 UI 界面更具响应性和加快应用启动速度 上作了很多工做。这其中应用启动关乎初次印象和是框架其余部分的压力源头,因此它是要解决的头等难题。编程

量化一切

咱们把Facebook的iOS版中的事件主页用RN从新实现(在更多标签页下点击事件进入查看)。这是个很是好的用于测试性能的例子,由于原生版已经作了大量的优化工做,并且该页面也是很是好的典型列表交互的例子。react-native

接下来,咱们自动化的 CT-Scan 性能测试来帮助咱们自动定位到咱们须要到的标签页。而后反复打开和关闭事件主页50次。在每次交互中,咱们可以记录下从点击事件按钮到事件主页可以被完整显示的时间,咱们也添加更多详细的性能埋点来告诉咱们启动过程哪些步骤是缓慢或消耗CPU的缓存

下面是咱们记录和测量的一些步骤的大体描述:性能优化

1 原生启动:初始化JavaScript虚拟机和其余一些原生模块(如磁盘缓存,网络,UI管理器等)服务器

2 JS初始化和依赖加载:从手机存储中读取被压缩的JS代码,加载到JavaScript虚拟机,从而解析和产生字节码,加载相关的依赖网络

3 取数据前:加载和执行事件主页的应用代码,构建Relay的查询语句,而后触发取数据。

4 取数据:从手机磁盘缓存读取数据

5 JS渲染:初始化全部相关的React组件,把它们发送到原生的UI管理器模块来显示。

6 原生渲染:在shadow线程中先经过根据 FlexBox 布局计算视图大小。而后在主线程中建立和定位这些视图。

咱们根据于此的黄金法则是:永远不要忘了回归测试。咱们持续的运行它来追踪性能提高和功能回归。开发者在提交改动的代码以前用它对特定的提交作运行和详细的性能分析。其余的一些测试也须要被一样的方式创建来衡量诸如功能性能和内存使用等

启动时发生了什么

当咱们设置好自动性能追踪,咱们须要一个工具来给咱们更多细节来决定启动过程当中的那些部分须要优化。咱们在咱们框架里添加详细的启动/暂停的性能锚点,收集数据,使用 catapult 查看器来定位热点和阻塞线程间交互的。也能够从开发者菜单下触发开始对咱们应用的性能分析。

在RN中,代码是在JavaScript线程中执行的。每次你要写数据到磁盘,在一次网络请求,或者取一些其余原生的资源(如摄像机),你的代码都须要调用原生模块。当你要渲染力你的 React 组件,它们会被转发到界面管理器的原生模块中,它在主线程中来执行布局和建立相应的视图。桥协议来转发请求到原生模块和被回调到你的JS代码(若是须要)。在RN中,全部原生的调用必须是异步的来避免阻塞主线程和JS线程。

在下面的事件组件的启动可视化图中,咱们能够看到应用在 JS 队列中运行,为了显示事件列表,触发了相关的缓存读取(在本地存储队列中被异步触发)。一旦它取得了缓存数据,应用在 JS 队列用 React 渲染事件单元格,接着又传给栅格队列来布局和最终传给主队列来建立视图显示。这个例子展现了多个缓存读取(组合成单个经常使用读取操做能够作到更快)和一些React在JS线程上的渲染操做能够被统一合并。

性能提高

下面是那些咱们在实施过程当中最重要的性能和时序安排的提高作法,同时配有对应代码提交的连接。

启动时少作些

安排合适时机执行

  • 懒加载
  • Relay的增量缓存读取:Relay一开始是web项目而生因此仅仅把请求响应放在内存中 – 要从磁盘读取的第一个请求的缓存响应须要从磁盘中读取所有的缓存到内存中。经过只读取知足特定查询请求的缓存,咱们能够显著减小 I/O 负载和原生到JS桥的流量。
  • 不用批量桥协议调用,要批量Relay调用:一开始咱们认为经过把JS请求批量发送给原生模块能够减小调用原生到JS桥的负载,可是性能分析告诉咱们JS和原生间的桥调用根本不是性能瓶颈。事实上,UI界面或缓存读取的批量操做的延迟也会延迟原生线程的操做,从而影响应用性能。在其余case上,注入Relay用于拉取多个键值数据的缓存读取,经过批处理能够有显著提高。
  • 更早的界面填充
  • 懒加载原生模块
  • 对文本组件的触摸作懒绑定:绑定触摸事件回调会须要很多时间。因此咱们如今仅仅先绑定触摸开始事件touch down event(就是当你第一次触摸对象时)而后只当你触摸对象后才开始绑定其余回调函数,而不是一开始就所有绑定对调
  • 延迟流行事件的查询:

为光速作准备

几个月前,事件主页的启动在 iPhone5 上须要2秒。通过咱们在RN上的大量性能优化工做,在伦敦,门洛帕克和纽约的RN,React和Relay团队,事件主页的启动被加快了一倍。并且大部分咱们实施的优化是在RN的框架层的,这就意味着开发者们的RN应用也会自动得益于这些工做(当他们把应用迁移到最新版本的RN下

这些优化才仅仅是个开始:咱们会继续在整个栈的各个部分都开展工做,从JavaScript代码解析时间到数据拉取性能。同时,大家也能够给社区贡献,学习如何让应用更快,在社区论坛提出你可能遇到的任何问题。QQ技术交流群290551701

相关文章
相关标签/搜索