美文网首页
React Native 性能调优(一)—— 前置篇

React Native 性能调优(一)—— 前置篇

作者: 华健_4106 | 来源:发表于2019-08-04 09:14 被阅读0次

使用 React Native 替代基于 WebView 的框架来开发 App 的一个强有力的理由,就是为了使 App 可以达到每秒 60 帧(足够流畅),并且能有类似原生 App 的外观和手感。因此我们也尽可能地优化 React Native 去实现这一目标,使开发者能集中精力处理 App 的业务逻辑,而不用费心考虑性能。但是,总还是有一些地方有所欠缺,以及在某些场合 React Native 还不能够替你决定如何进行优化(用原生代码写也无法避免),因此人工的干预依然是必要的。                                                                                                                                   ——官网性能说明

本文(分前置篇,理论篇,实战篇)的目的是总结一些性能方面的问题,以及探讨这些问题产生的原因和推荐的解决方法。希望对你有所帮助。

在处理性能优化之前,我们需要了解RN运行的一定原理,从而了解卡顿的原因从而解决之。

1.关于“帧”你所需要知道的

老一辈人常常把电影称为“移动的画”,是因为视频中逼真的动态效果其实是一种幻觉,这种幻觉是由一组静态的图片以一个稳定的速度快速变化所产生的。我们把这组图片中的每一张图片叫做一帧,而每秒钟显示的帧数直接的影响了视频(或者说用户界面)的流畅度和真实感。iOS 设备提供了每秒 60 的帧率,这就留给了开发者和 UI 系统大约 16.67ms 来完成生成一张静态图片(帧)所需要的所有工作。如果在这分派的 16.67ms 之内没有能够完成这些工作,就会引发‘丢帧’的后果,使界面表现的不够流畅。

下面要讲的事情可能更为复杂:请先调出你应用的开发菜单,打开Show FPS Monitor. 你会注意到有两个不同的帧率.

show FPS Monitor

JS 帧率(JavaScript 线程)

对大多数 React Native 应用来说,业务逻辑是运行在 JavaScript 线程上的。这是 React 应用所在的线程,也是发生 API 调用,以及处理触摸事件等操作的线程。更新数据到原生支持的视图是批量进行的,并且在事件循环每进行一次的时候被发送到原生端,这一步通常会在一帧时间结束之前处理完(如果一切顺利的话)。如果 JavaScript 线程有一帧没有及时响应,就被认为发生了一次丢帧。 例如,你在一个复杂应用的根组件上调用了this.setState,从而导致一次开销很大的子组件树的重绘,可想而知,这可能会花费 200ms 也就是整整 12 帧的丢失。此时,任何由 JavaScript 控制的动画都会卡住。只要卡顿超过 100ms,用户就会明显的感觉到。

这种情况经常发生在老的Navigator导航器的切换过程中:当你 push 一个新的路由时,JavaScript 需要绘制新场景所需的所有组件,以发送正确的命令给原生端去创建视图。由于切换是由 JavaScript 线程所控制,因此经常会占用若干帧的时间,引起一些卡顿。有的时候,组件会在componentDidMount函数中做一些额外的事情,这甚至可能会导致页面切换过程中多达一秒的卡顿。(非常常见的跳转卡顿,后面会进行详解)

另一个例子是老的触摸事件的响应:如果你的 JavaScript 进程线程处理一个跨越多个帧的工作,你可能会注意到TouchableOpacity的响应被延迟了。这是因为 JavaScript 线程太忙了,不能够处理主线程发送过来的原始触摸事件,结果TouchableOpacity就不能及时响应这些事件并命令主线程的页面去调整透明度了。

UI 帧率(主线程)

很多人会注意到,NavigatorIOS的性能要比老的纯 JS 实现的Navigator好的多。原因就是它的切换动画是完全在主线程上执行的,因此不会被 JavaScript 线程上的掉帧所影响。(因此对于混合开发进入测试阶段,性能调优最好使用Android并跨越多个Android版本系统方能暴露更多卡顿的代码,从而更全面的优化)

同样,当 JavaScript 线程卡住的时候,你仍然可以欢快的上下滚动ScrollView,因为ScrollView运行在主线程之上(尽管滚动事件会被分发到 JS 线程,但是接收这些事件对于滚动这个动作来说并不必要)。

RN在性能上做了一系列优化,采用virtual DOM 、以及在传统DOM diff算法的基础上按着自己的策略进行了改进,但RN毕竟只是原生APP的一个线程而已,RN和原生需要batch-bridge进行通讯,最终RN的代码还是要通过js解析引擎(JavaScriptCore)来解析并调用原生代码来执行,这就决定了RN不可能超越原生。值得提一句,flutter没有使用这一套机制,Flutter只关心向 GPU提供视图数据,GPU的 VSync信号同步到 UI线程,UI线程使用 Dart来构建抽象的视图结构,这份数据结构在 GPU线程进行图层合成,视图数据提供给 Skia引擎渲染为 GPU数据,这些数据通过 OpenGL或者 Vulkan提供给 GPU。(不扯远了,有兴趣的朋友可以自行查看浅析flutter原理

2.通过两张图来了解下重新渲染的变化过程及原理

第一张生命周期经典图(熟悉的可以略过):

React Native生命周期

第二张重渲染详解图:我们优化思路主要针对讨论下图中哪些过程及细节可以优化

相关文章

网友评论

      本文标题:React Native 性能调优(一)—— 前置篇

      本文链接:https://www.haomeiwen.com/subject/omdedctx.html