关于帧
在讲述如何优化性能之前,我们要先了解一下性能的概念。对一个从未接触过相关概念的人来说,所谓的性能就是当你去使用App的时候很流畅,点击、跳转等交互效果反应很快,而且很顺滑。这是感性的角度,那么理性的角度或者数据的角度看待呢?
这里要介绍一个概念: 帧率。我们知道,所谓的动画或者电影,其实归根结底就是在一秒内快速闪过多张不同的图片,如果快到一定的程度,肉眼会误以为里面的动画都是连贯的。在iOS等设备,标准是每秒60帧(即每秒连续展示60张图片),这个标准足以保证用户的体验。
系统会每16.6毫秒询问你下一帧的数据,如果你正在处理比较复杂的任务,则系统会默认这一帧内容保持不变,即出现了丢帧的现象。如果丢的帧比较多,则界面会看起来卡顿,比如用户点击了按钮,但是没有反馈。
对React Native
来说,帧分为两种: JavaScript
帧和主线程帧(UI
帧)
JavaScript帧
React Native
大部分的业务处理,都是在JavaScript
帧中进行,包括API调用和触摸等交互的处理。那么当处理比较复杂的任务,比如setState
然后render
,则很可能会丢帧。或者做由JS处理的动画时,也极容易出现丢帧卡顿。
主线程帧(UI
帧)
iOS的主线程是UI线程,所以在iOS的UI效果基本是非常出色的,这也是为什么NavigatorIOS
比Navigator
的性能好很多的原因(NavigatorIOS
是主线程处理,而Navigator
是JS线程处理)
如何查看帧数据
我们可以通过打开RN
的Debug
菜单,然后选择Show Perf Monitor
来查看当前页面的JS
帧和主线程帧。
好了,性能的定义和我们评估性能标准已经知道了,下面我们来看下影响性能的因素。
影响性能的因素和提升方案
宽泛的原因
我们先抛开RN或者iOS的前端框架,看下对一台带屏幕的设备来说,影响性能的原因有哪些。
我们用金字塔模式来看,首先最明显的有两个
- 设备性能
- 程序设计
从根本上来说,设备性能是最大的瓶颈,不过这个我们程序员暂时无能为力。而程序设计可能引起性能差的有哪些呢?判断程序设计的一个标准就是复杂度, 而复杂度又分为两个:
- 时间复杂度
- 空间复杂度
这时我们的性能因素树是:
- 设备性能
- 程序设计
- 时间复杂度
- 空间复杂度
在设备空间充足的情况下,主要的影响就是时间复杂度,而时间复杂度高的原因有几个:
- 前端方案不合理,UI层次或者顺序设计不合理,浪费性能
- 使用的算法过于复杂
我们挨个说下这两个个问题,前端方案是最容易造成性能不好的原因,比如我们有屏幕上有几个区域,互相之间没有影响,而由于不合理的设计,在一个区域变化的时候,要刷新这个界面,就会出现卡顿。一个合理的前端方案,应该是尽可能减少页面的刷新频率和刷新范围,保证每帧的计算是相对小的。
而算法过于复杂,则是算法消耗的时间太长,影响了UI的渲染。比如使用了圈复杂度非常高的算法,或者有大量的数据要不停地计算。
这样我们的性能因素树变成了:
- 设备性能
- 程序设计
- 空间复杂度
- 时间复杂度
- 前端方案不合理,UI层次或者顺序设计不合理,浪费性能
- 使用的算法过于复杂
可能还会有人觉得网络等原因会造成卡顿,但是我觉得如果交互设计良好,网络状况不好的话,只会影响数据出现的时间长,而不会造成页面的卡顿。
看完了比较普遍宽泛的原因,我们看下针对RN的
RN的特有原因
JS Bridge的效率
虽然官方的文章里没有写,但是从我测试看到的数据来看,虽然RN的性能比较接近Native,但是因为JS是运行在子线程中的,所以处理大量数据或者动画的时候,JS的帧数会比较少。
这个我们暂时无能为力
动画和Touchable组件在JS线程中运行
Animated和Touchable系列组件都是在JS中运行,所以在处理复杂动画或者复杂操作的时候,会出现卡顿。
这里给几个建议:
关于导航
- iOS上使用
NavigatorIOS
替换Navigator
,同时,react也推出了新的Navigation库希望解决导航卡顿的问题 - push的新界面的动画,使用
InteractionManager
,就是在导航动画结束后执行新的动画,而不是同时执行
- iOS上使用
关于动画
- 如果
Animated
的效果不能接受,使用LayoutAnimation
,它是基于Core Animation
- android上面尽量少用动画(真的很卡0_0)
- 如果
其他建议
- 使用PureComponent
- 使用
shouldComponentUpdate
函数,这个函数默认返回true,但是我们可以通过自定义来优化重新绘制的逻辑 - release 去除console.log()
global.console = {
info: () => {},
log: () => {},
warn: () => {},
debug: () => {},
error: () => {},
};
}
最重要的建议
- 仔细考虑UI的设计
- 仔细考虑UI的设计
- 仔细考虑UI的设计
一个好的UI设计方案,是可以抵过上面所有的建议加起来的效果的,所以一定要仔细考虑再动手。
参考: