性能之谜,Native性能之谜

关于作者:ThoughtWorks

图片 1

ThoughtWorks是一家全球IT咨询公司,追求卓越软件质量,致力于科技驱动商业变革。擅长构建定制化软件产品,帮助客户快速将概念转化为价值。同时为客户提供用户体验设计、技术战略咨询、组织转型等咨询服务。

个人主页 ·
我的文章 ·
84 ·
  

图片 2

React Native的工作原理

在React
Native的应用中,存在着两个不同的技术王国:JS王国和Native王国。应用在启动时会先进行双向注册,搭好桥,让两个王国知道彼此的存在,以及定义好彼此合作的方式:

图片 3

(图片来源: )

然后,在应用的实际运行过程中,两个技术王国通过搭好的桥,彼此合作完成用户功能:

图片 4

(图片来源:

因此,React
Native的本质是在两个技术王国之间搭建双向桥梁,让他们可以相互调用和响应。那么就可以把上图简化一下:

图片 5

React Native 性能之谜

2017/04/14 · JavaScript
· React Native

本文作者: 伯乐在线 –
ThoughtWorks
。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者。

在 PhoneGap、RubyMotion、Xamarin、Ionic 一众跨平台开发工具中,React
Native能够杀出一条血路,获得目前这么大的影响力,除了React社区生态圈的加持和Facebook的大力推广以外,另外一个最主要的原因就是其在开发效率和应用性能方面取得了一个比较好的平衡:

  • 开发效率通过JS工程实践,逻辑跨平台复用得到极大提升
  • 性能则通过全Native的UI层得到满足

不过,虽说框架提供了这个平衡能力,平衡点的选择却掌握在开发者手中,本文将从React
Native的性能角度来看看应该如何掌握这个平衡点。

React Native的性能瓶颈

经过上面的分析,我们就可以把一个React
Native应用分成三个部分:Native王国、Bridge、JS王国。当应用运行时,Native王国和JS王国各自运行在自己独立的线程中:

Native王国:

  • 运行在主线程上(可能会有些独立的后台线程处理运算,当前讨论中可忽略)
  • iOS平台上运行Object-C/Swift代码,Android平台上运行Java/Kotlin代码
  • 负责处理UI的渲染,事件响应。

JS王国:

  • 运行在JS引擎的JS线程上
  • 运行JS代码
  • 负责处理业务逻辑,还包括了应该显示哪个界面,以及如何给页面加样式。

在Native王国中,经过谷歌、苹果公司多年的优化调整,Native代码能够非常快速的运行在设备上。在JS王国中,JS代码作为脚本语言,也能够很快速的运行在JS引擎上,这两边独立来看都不会有性能问题。性能的瓶颈只会出现在从一个王国转入另一个王国时,尤其是频繁的在两个王国之间切换时,两个王国之间不能直接通信,只能通过Bridge做序列化和反序列化,查找模块,调用模块等各种逻辑,最终反应到应用上,就是UI层用户可感知的卡顿。
因此,对React
Native的性能控制就主要集中在如何尽量减少Bridge需要处理的逻辑上。

那么,什么情况下会需要Bridge处理逻辑呢?

  1. UI事件响应
    所有的UI事件都发生在Native侧,会以事件的形式传递到JS侧。这个过程非常简单,也不会涉及大量的数据转移。在React
    Native应用中,业务逻辑,应用状态,数据都在JS侧,所以UI事件只是一个触发器,不会有性能问题。
  2. UI更新:前面已经说过JS负责决定应该展示哪个界面,以及如何样式化界面,因此UI更新的发起方是JS侧,更新时会向Native侧同步大量的UI结构和数据,这类更新经常出现性能问题,尤其是在界面复杂、变动数据大,或者做动画、变动频繁时。
  3. UI事件响应和UI更新同时出现:在UI更新时,结构变化不大,则性能问题不大;但是如果这时又有UI事件触发JS侧逻辑处理,而该逻辑处理又比较复杂,耗时较长,导致JS侧没有时间片处理与Native侧数据同步时,也会发生性能问题。

React Native的性能瓶颈

经过上面的分析,我们就可以把一个React
Native应用分成三个部分:Native王国、Bridge、JS王国。当应用运行时,Native王国和JS王国各自运行在自己独立的线程中:

Native王国:

  • 运行在主线程上(可能会有些独立的后台线程处理运算,当前讨论中可忽略)
  • iOS平台上运行Object-C/Swift代码,Android平台上运行Java/Kotlin代码
  • 负责处理UI的渲染,事件响应。

JS王国:

  • 运行在JS引擎的JS线程上
  • 运行JS代码
  • 负责处理业务逻辑,还包括了应该显示哪个界面,以及如何给页面加样式。

在Native王国中,经过谷歌、苹果公司多年的优化调整,Native代码能够非常快速的运行在设备上。在JS王国中,JS代码作为脚本语言,也能够很快速的运行在JS引擎上,这两边独立来看都不会有性能问题。性能的瓶颈只会出现在从一个王国转入另一个王国时,尤其是频繁的在两个王国之间切换时,两个王国之间不能直接通信,只能通过Bridge做序列化和反序列化,查找模块,调用模块等各种逻辑,最终反应到应用上,就是UI层用户可感知的卡顿。
因此,对React
Native的性能控制就主要集中在如何尽量减少Bridge需要处理的逻辑上。

那么,什么情况下会需要Bridge处理逻辑呢?

  1. UI事件响应
    所有的UI事件都发生在Native侧,会以事件的形式传递到JS侧。这个过程非常简单,也不会涉及大量的数据转移。在React
    Native应用中,业务逻辑,应用状态,数据都在JS侧,所以UI事件只是一个触发器,不会有性能问题。
  2. UI更新:前面已经说过JS负责决定应该展示哪个界面,以及如何样式化界面,因此UI更新的发起方是JS侧,更新时会向Native侧同步大量的UI结构和数据,这类更新经常出现性能问题,尤其是在界面复杂、变动数据大,或者做动画、变动频繁时。
  3. UI事件响应和UI更新同时出现:在UI更新时,结构变化不大,则性能问题不大;但是如果这时又有UI事件触发JS侧逻辑处理,而该逻辑处理又比较复杂,耗时较长,导致JS侧没有时间片处理与Native侧数据同步时,也会发生性能问题。

React Native的性能优化措施

前面已经解释了React Native的性能瓶颈会在什么地方,React
Native官方也知道这些,其在React
Native中提供了一些性能优化措施帮助开发者克服这些性能问题:

  1. 框架自带的React基于Virtual
    Dom的Diff算法保证了UI变动时传递的只是变化的UI部分,尽量减少需要同步的数据。
  2. 通过Direct
    Manipulation的方式直接在底层更新了Native组件的属性,从而避免渲染组件结构和同步太多视图变化所带来的大量开销。这样的确会带来一定的性能提升,同时也会使代码逻辑难以理清,而且并没有解决从JS侧到Native侧的数据同步开销问题。因此这个方式官方都不再推荐,更推荐的做法是合理使用setState()和shouldComponentUpdate()方法解决这类问题。
  3. 在遇到动画性能问题时,可以使用Annimated类的库,一次性把如何变化的声明发送到Native侧,Native侧根据接收到的声明自己负责接下来的UI更新。不需要每帧的UI变化都同步一次数据。
  4. Native和JS混编,把会大量变化的组件做成Native组件,这样UI的变更数据直接在Native侧自己处理了,无需通过Bridge,而不变的内部组件因为没有数据更新需要同步,所以也不会使用到Bridge。框架提供的NavigatorIOS相对于Navigator的性能提升就是这种做法。
  5. 遇到事件响应和UI更新同时发生导致的性能问题时,可以使用Interaction
    Manager把那些耗时较长的工作安排到所有互动或动画完成之后再进行。

React Native的性能优化措施

前面已经解释了React Native的性能瓶颈会在什么地方,React
Native官方也知道这些,其在React
Native中提供了一些性能优化措施帮助开发者克服这些性能问题:

  1. 框架自带的React基于Virtual
    Dom的Diff算法保证了UI变动时传递的只是变化的UI部分,尽量减少需要同步的数据。
  2. 通过Direct
    Manipulation的方式直接在底层更新了Native组件的属性,从而避免渲染组件结构和同步太多视图变化所带来的大量开销。这样的确会带来一定的性能提升,同时也会使代码逻辑难以理清,而且并没有解决从JS侧到Native侧的数据同步开销问题。因此这个方式官方都不再推荐,更推荐的做法是合理使用setState()和shouldComponentUpdate()方法解决这类问题。
  3. 在遇到动画性能问题时,可以使用Annimated类的库,一次性把如何变化的声明发送到Native侧,Native侧根据接收到的声明自己负责接下来的UI更新。不需要每帧的UI变化都同步一次数据。
  4. Native和JS混编,把会大量变化的组件做成Native组件,这样UI的变更数据直接在Native侧自己处理了,无需通过Bridge,而不变的内部组件因为没有数据更新需要同步,所以也不会使用到Bridge。框架提供的NavigatorIOS相对于Navigator的性能提升就是这种做法。
  5. 遇到事件响应和UI更新同时发生导致的性能问题时,可以使用Interaction
    Manager把那些耗时较长的工作安排到所有互动或动画完成之后再进行。

不过,虽说框架提供了这个平衡能力,平衡点的选择却掌握在开发者手中,本文将从React
Native的性能角度来看看应该如何掌握这个平衡点。

发表评论

电子邮件地址不会被公开。 必填项已用*标注