React Native应用性能瓶颈分析和优化

更新日期: 2020-07-09阅读: 2.4k标签: native

导语

本文结合react Native跨平台框架的应用工作原理,分析性能瓶颈,分享实践中的优化性能问题内容输入。


背景

随着移动互联网的高速发展,在很多的业务场景下,传统的纯原生开发已经不能满足日益增长的业务需求。为了提升开发效率,使APP具备动态化能力,同时提升用户体验,项目中引入了React Native框架。使用过程中发现部分React Native页面性能存在一些问题,尤其是复杂页面渲染性能较差,于是决定深入调研和解决这些问题。


React Native工作原理

1.概览

React Native的页面使用JavaScript编写,在JavaScript虚拟机里运行,JavaScript标签/组件最终会被解析为iOS和Android端的View,从而实现跨平台。JavaScript代码运行在一个后台线程,也就是JavaScript虚拟机运行的线程,而不是主线程。这样设计的初衷是为了防止主线程被JavaScript代码执行阻塞,保持UI界面更流畅。JavaScript线程和主线程之间的交互是异步的。



2. 定位问题和优化思路

一个React Native页面生命周期,一般包括初始化,加载数据,页面渲染,事件响应,页面刷新,销毁等几个过程。接下来将逐一分析这几个过程中,可能存在的性能瓶颈和优化思路。

2.1 页面初始化

页面初始化时,React Native框架会先从本地文件系统加载JavaScript Bundle文件,并执行文件里的JavaScript代码。

性能瓶颈:

⦁ JavaScript文件加载

⦁ JavaScript代码执行

优化思路:

⦁ JavaScript Bundle拆包,把公用基础代码抽取出来,每次打开页面,只加载业务代码,减少文件大小,同时也可以减少代码执行量。

⦁ 可以考虑使用JS Byte Code,加快JavaScript代码执行速度。

2.2 加载数据

React Native页面加载数据,一般由JavaScript端组装参数,Native端发起网络请求,再将结果返回给JavaScript端。

性能瓶颈:

⦁ 一般JavaScript端组装参数的时候,可能需要从Native端读取数据,会涉及一次或多次JavaScript-Native通信,这个过程是异步的,如果涉及到数据传输,则会经历JS Object->String->Native Object和Native Object->String->JS Object(JSON序列化和反序列化)两个过程,比较耗时。

⦁ 网络请求数据

优化思路:

⦁ 尽量避免每次加载数据时,JavaScript端都要从Native端读取数据,最好有一套比

2.3   页面渲染

先看一下React Native页面渲染过程


React Native页面渲染过程

⦁APP启动,主线程初始化React Native框架。

⦁主线程创建Bridge,加载JavaScript文件。

⦁JavaScript线程解析执行JavaScript代码,生成dom Tree结构。

⦁Shadow Thread处理样式信息,生成layout数据,生成Shadow Tree。

⦁主线程根据Shadow Tree的层级关系,和layout数据,创建Native View。

⦁主线程渲染Native View。

性能瓶颈:

React Native页面渲染,经历了多次线程切换和JavaScript-Native通信,组件渲染也是顺序进行,虽然是批量操作,但是无法并发渲染。

优化思路:

尽量减少页面的组件数量和嵌套关系,复杂的页面结构,大量的组件嵌套,首次渲染的时候,会非常慢,会出现明显白屏现象。

2.4 事件响应


⦁Native捕捉到Touch,Timer,网络等Event。

⦁Native负责组装数据。

⦁Native通过Bridge将数据序列化为JSON String,传递给JavaScript。

⦁JavaScript反序列化JSON为JavaScript Object,处理Event。

⦁JavaScript组装数据,序列化JSON String,通过Bridge 回调Native方法。

⦁Bridge反序列化JSON为Native Object,执行Native方法。

⦁刷新UI。

性能瓶颈:

在React Native中,Native端负责捕捉事件,通知JavaScript端处理,处理完毕之后,再通知Native端刷新UI。一次事件处理,包括了两次JavaScript-Native通信。

而JavaScript是单线程执行的,如果JavaScript处理逻辑太多,则会出现用户响应不及时。

优 化思路 

在一些事件交互比较多的场景(比如长列表滑动/动画),尽量把大量计算逻辑(cell生命周期维护/动画每一帧属性计算)移到Native端执行,减少JavaScript线程的工作量,避免每一帧都要和Native进行通信。

React Native性能优化实践

1. 信息中心

为了减少JavaScript-Native通信的次数,我们设计了信息中心模块。

JavaScript环境和Native环境各自维护一些状态数据,这些数据互补可见,但是双方都有读取和同步状态数据需要,为了减少JavaScript-Native数据交互次数,我们开发出信息中心模块。

信息中心的目的是建立一种数据同步机制,减少JavaScript-Native的不必要数据交互。


我们把需要两端共享的数据分为不变数据,定时更新数据,实时更新数据三种类型,针对不同的数据采用不同的数据同步策略。

⦁不变数据:设备信息,系统信息,软件信息等。

⦁定时更新数据:对精确度要求不高的数据,比如位置信息。

⦁实时更新数据:登录态,网络状态等。

避免每次读取JavaScript或Native数据时,都发起一次数据交互请求,

开发者也不必再关系数据同步问题,降低了开发复杂度。


2. Native驱动的列表页

为了解决React Native长列表页,内存占用大,卡顿,容易白屏的问题,我们设计Native驱动列表组件。

React Native列表页,用户交互比较多,列表Cell的生命周期(创建,展示,刷新,移除,销毁)都是JavaScript端维护,Native端把列表的滚动状态实时传递给JavaScript端,JavaScript维护Cell的生命周期,再通知Native端刷新UI,多次事件交互,导致RN原生列表页性能差,快速滑动有白屏现象。为了减少,JavaScript线程计算压力,减少JavaScript-Native交互,我们推出了Native驱动列表。


Native驱动的列表页

Native驱动列表的优势在于,JavaScript只提供数据源和Cell模版,Cell生命周期由Native维护,减少JavaScript线程计算压力,渲染更快,没有JavaScript-Shadow-Main线程切换,减少JavaScript-Native的事件交互次数,支持Cell回收/重用,节省内存,提升速度,流畅度更高,接近Native原生列表,兼容旧系统的JavaScript Cell模版。


总结

React Native应用JavaScript线程和主线程,任何一个线程被阻塞,都会导致卡顿和掉帧现象。JavaScript端在处理完事件响应或数据更新时,需要通知Native端重新渲染UI,这个过程是异步的,因此会略有延迟。在React Native现在的框架下,主要优化思路就是减少不必要的组件重绘,减少不必要的JavaScript-Native通信,减少JavaScript线程的计算压力,可以把一些原先在JavaScript执行的任务,移植到Native端执行,实现由JavaScript驱动到Native驱动,都会带来显著性能提升。

参考文献

1.   https://www.freecodecamp.org/news/how-react-native-constructs-app-layouts-and-how-fabric-is-about-to-change-it-dd4cb510d055/

2.   https://tech.showmax.com/2018/05/react-native-or-not-react-native/

3.   https://reactnative.cn/docs/performance/

4.   https://www.simform.com/react-native-app-performance/

作者简介
禹潇潇  58集团  HRG技术部 

链接: https://www.fly63.com/article/detial/9486

来聊聊怎么写react-native上的样式吧

在react-native上是怎么写样式的吧,和传统的web不一样的是,在react-native上面是没有css代码,不过得益于Yoga,我们可以在客户端上像写css一样的去书写我们的样式。

WKWebView 里 JS 和 native 通信的例子

初始化 wkwebview,设置 message handler,native 端注册了 testecho 的messageHandler,实现 WKScriptMessageHandler协议,执行JS 代码 ,所以 JS 可以通过 window.webkit.messageHandlers.testecho.postMessage 来回调客户端,和文档中说的一样

由使用request-promise-native想到的异步处理方法

因为js语言的特性,使用node开发程序的时候经常会遇到异步处理的问题。对于之前专长App开发的我来说,会纠结node中实现客户端API请求的“最佳实践”。下面以OAuth2.0为场景,需要处理的流程:获取access token、使用获取到的token,发起API请求、处理API数据

React Native项目使用react-apollo实现更新缓存的两种方式

GraphQL是一个API查询语言,他可以将使用PostgreSQL写的server代码自动生成Query或者Mutation,非常的方便。而Apollo Client就是一个强大的JavaScript GraphQL客户端。对于cache,在Apollo Client中有着强大的管理策略。

11个React Native组件库

React Native 是 Facebook 2015年开源的 Javascript 框架,旨在使用 Javascript 高效开发手机端 App。根据大众的需求,我们列出了一个有用的React-Native UI库列表,可以帮助你更好地入门React Native。

React Native 传参的几种方式

在React Native 中由于业务的需要, 我们往往要在诸多的页面间,组件之间做一些参数的传递与管理, 在这里我总结了几大经过验证,稳定好用的方式给大家

如何正确选型,React Native 还是 Native?

随着 H5 标准的发布以及推广,使得移动应用的开发也受到了很大影响,出于效率、成本等原因,移动应用的开发不再完全依赖于 “原生”。近日越发火热的混合应用(Hybrid App)介于 Web 应用和原生应用之间

react-native报错Cannot get property packageName on null object

react-native打包安卓apk的时候,报错Cannot get property packageName on null object,完全没有头绪,研究了半天才发现竟然是因为package.json里面scripts自定了命令导致的,无法理解为何会影响安卓打包并且报错packageName null

React Native 添加 Redux 支持

之前写的项目都是人家编写好的脚手架,里面包含项目所需的环境文件,但由于有些东西用不到打包增加软件体积,所以自己从头搭建个环境。是基于 Native Base + react-navigation + Redux 的 React Native 脚手架,现在项目环境如下

Flutter platform view 使用篇

Flutter作为备受关注的跨平台的开发框架,长远来看,前景肯定是比较好的,在其基础组件还未完善与成熟之前,能够高效的复用现有的native组件,是比较合适的方案。官方提供了Plugin的方式,允许将一个成熟的native组件(比如mapview)

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!