当前位置: 首页 > 科技观察

ReactNative性能与效率平衡之谜

时间:2023-03-19 11:50:47 科技观察

在PhoneGap、RubyMotion、Xamarin、Ionic等跨平台开发工具中,目前ReactNative能杀出一条血路并获得如此大的影响力,除了支持React社区生态的发展除了Facebook的大力推广外,另一个主要原因是它在开发效率和应用性能方面取得了比较好的平衡:通过JS工程实践大大提高了开发效率,逻辑交叉平台重用大大提高。不过Native的UI层是满足的。虽然框架提供了这种平衡能力,但是平衡点的选择权在开发者手中。本文将从ReactNative性能的角度来看如何把握这个平衡点。一、ReactNative的工作原理在ReactNative的应用中,有两个不同的技术王国:JS王国和Native王国。应用启动时,首先会进行双向注册,搭建桥梁,让两个王国互相知道对方的存在,并定义彼此合作的方式:(图片来源:http://t.cn/RXwes3j)然后,在应用程序实际运行过程中,两个技术王国通过搭建的桥梁相互协作完成用户功能:(来源:http://t.cn/R5xMqZ0)因此,ReactNative的本质就是要在两个技术王国之间搭建一座双向的桥梁,让它们可以互相调用,互相响应。那么我们可以把上图简化一下:2.ReactNative的性能瓶颈经过上面的分析,我们可以把一个ReactNative应用分为三个部分:NativeKingdom、Bridge和JSKingdom。应用运行时,NativeKingdom和JSKingdom在各自独立的线程中运行:1.NativeKingdom:在主线程上运行(可能会有一些独立的后台线程处理操作,目前讨论中可以忽略)runiOS平台上的Object-C/Swift代码,Android平台上运行的Java/Kotlin代码负责UI渲染和事件响应。2.JS王国:在JS引擎的JS线程上运行JS代码。它负责处理业务逻辑,包括应该显示哪个界面,如何给页面添加样式。在Native王国,经过谷歌和苹果多年的优化调整,Native代码可以非常快速的在设备上运行。在JS王国中,JS代码作为一种脚本语言,也可以非常快速的运行在JS引擎上,单独来看也不会有性能问题。只有在从一个王国转移到另一个王国时才会出现性能瓶颈,尤其是两个王国之间频繁切换时,两个王国无法直接通信,只能通过Bridge进行序列化和反序列化、查找模块、调用模块等逻辑,最后对应用程序做出反应,这是用户可感知的UI层滞后。因此,ReactNative的性能控制主要集中在如何最小化Bridge需要处理的逻辑上。那么,Bridge在什么情况下需要处理逻辑呢?UI事件响应:所有的UI事件都发生在Native端,会以事件的形式传递给JS端。该过程非常简单,不涉及大量数据传输。在ReactNative应用中,业务逻辑、应用状态和数据都在JS端,所以UI事件只是一个触发器,不会有性能问题。UI更新:上文提到,JS负责决定应该显示哪个界面,以及如何给界面设置样式。因此,UI更新的发起者是JS端。更新时,大量的UI结构和数据会同步到Native端。这类更新经常会出现性能问题,尤其是在界面复杂,数据变化量大,或者执行动画变化频繁的情况下。UI事件响应和UI更新同时发生:UI更新时,结构变化不大,性能问题不大;但是如果此时又有UI事件触发JS端逻辑处理,逻辑处理比较复杂,耗时长,导致JS端没有时间片处理,Native端没有数据同步,性能也会出现问题。3、ReactNative的性能优化措施已经说明了ReactNative的性能瓶颈会在哪里。ReactNative官方也知道这一点。他们在ReactNative中提供了一些性能优化措施来帮助开发者克服这些性能问题:1.框架内置的React基于VirtualDomDiff算法保证在UI变化时只传输变化的UI部分,最小化需要同步的数据。2、通过DirectManipulation直接在底层更新Native组件的属性,避免渲染组件结构和同步太多视图变化带来的大量开销。这样确实会带来一定的性能提升,但是也会导致代码逻辑难以梳理,也解决不了JS端到Native端的数据同步开销问题。因此官方不再推荐这种方式。比较推荐的方法是合理使用setState()和shouldComponentUpdate()方法来解决此类问题。3.当遇到动画性能问题时,可以使用Animated类库将如何变化的声明一次性发送给Native端,Native端根据收到的声明负责下一次的UI更新。无需每帧UI更改都同步数据。4.Native和JS混合,将变化比较大的组件做成Native组件,这样UI变化数据直接在Native端处理,不用经过Bridge,内部变化不大的组件不需要同步,因为没有数据更新。也不会使用桥接。这是框架提供的NavigatorIOS相比Navigator性能提升的方式。5.当遇到事件响应和UI更新同时引起的性能问题时,可以使用InteractionManager来安排那些耗时的任务,直到所有的交互或动画完成。4.探索性能与效率平衡的套路了解了ReactNative的性能瓶颈和优化措施后,可以大致总结出一个探索ReactNative开发效率和性能平衡的套路:第一步:全JS实现,之所以在技术选型上开始使用ReactNative,是为了保证开发的效率。在遇到性能问题之前,效率最大化是团队的一贯追求。第二步:从JS端进行性能优化对于那些明显涉及Bridge,需要大量处理逻辑的场景,比如动画,复杂的手势操作响应等,尽量使用优化过的库(例如:Animated),一旦将动画或数据的描述传递给Native,Native端就会根据语句执行。使用InteractionManager将耗时操作推迟到UI响应之后,处理那些JS操作耗时导致的UI响应性能问题。第三步:在真机上进行测试,检查性能问题。不要过早优化,找到问题点,一一处理。Step4:如果在JS端优化策略后在设备上仍然存在性能问题,可以用native的方式实现有问题的部分,这也是为什么建议ReactNative团队有10%左右有Native的原因开发商。这一步需要注意问题的隔离方法。假设一个场景:移动一个Container时,Container的UI同时发生变化,但是Container内部的内容没有变化。这种情况下,你只需要用Native来实现Container,Container里面的组件还是用JS实现的。【本文为专栏作者“ThoughtWorks”原创稿件,微信公众号:Thinkworker,转载请联系原作者】点此查看该作者更多好文