Open amandakelake opened 6 years ago
刚好看到链接挂了, 更新一下 React-Native通用化建设与性能优化- Web前端 腾讯IVWeb 团队社区
二、组件响应速度 2)requestAnimationFrame window.requestAnimationFrame - Web API 接口 | MDN 使用requestAnimationFrame(fn)在下一帧就立即执行回调,这样就可以异步来提高组件的响应速度;
请问下,这段为啥说下一帧立即执行回调能提高组件响应速度,咋感觉这是降低了响应速度啊
另外一个问题是您写了这么多性能优化方法,请问您是如何评价页面性能的,有相关数据指标吗,还是说靠感觉
这么多年了。。。期待更新
前言
最近在进行RN项目重构,通过查阅各种资料,从RN底层出发,思考总结了一些从react到react-native的性能优化相关问题
Performance · React Native 请先认真查看官方文档(英文文档)这一章节 前方高能请注意:Unbundling + inline requires这一节,中文文档木有!!!
先看看可能会导致产生性能问题的常见原因
这里先给出我自己的结论,然后会从底层原理开始理解为何要这样做,最后是每项方法的具体展开(未完待续)
这部分都不是死知识,可能哪天我又会有更广阔的思路与解决办法,或许会推翻现在的结论,所以本文会持续保持更新。。。
RN性能优化概述
谈性能之前,我们先了解一下RN的工作原理
通过RN我们可以用JS实现跨平台App,也就是FB说的write once, run everywhere
RN为我们提供了JS的运行环境,所以前端开发者们只需要关心如何编写JS代码,画UI只需要画到virtual DOM 中,不需要特别关心具体的平台
至于如何把JS代码转成native代码的脏活累活,RN底层全干了
RN的本质是把中间的这个桥Bridge给搭好,让JS和native可以互相调用
RN的加载流程主要为几个阶段
Dive into React Native performance | Engineering Blog | Facebook Code | Facebook
通过对FaceBook的ios版进行性能测试,得到上面的耗时图 可以看到,绿色的
JS Init + Require
占据了一大半的时间,这部分主要的操作是初始化JS环境:下载JS Bundle、运行JS BundleJS Bundle 是由 RN 开发工具打包出来的 JS 文件,其中不仅仅包含了RN 页面组件的 JS 代码,还有 react、react-native 的JS代码,还有我们经常会用上的redux、react-navigation等的代码,RN 非常简单的 demo 页面minify 之后的 JS Bundle 文件有接近 700KB,所以 JS Bundle文件大小是性能优化的瓶颈
假设我们有一个大型App,它囊括了非常多的页面,但是在常规使用中,很多页面甚至都不会被打开,还有一些复杂的配置文件以及很少使用的功能,这些相关的代码,在App启动的时候都是不需要的,那么,我们就可以考虑通过Unbundling拆包来优化性能
关于如何减少Bundle包的大小,目前主流的方法是拆分Bundle包,把框架代码和业务代码单独出来,框架代码非常大,因此要分离出来单独前置加载,而业务代码则变成很小的JS代码单独发布,下面提供一些前人的经验链接
但在拆包之前,FB官方还提了几条在此之前更应该做好的优化点
Doing less
Scheduling
React-Native通用化建设与性能优化 - Web前端 腾讯IVWeb 团队社区 不愧是腾讯,主要讲了通用化建设、bundle本地分包、项目线上性能分析几项 RN分包之Bundle改造 RN 打包那些事儿 | YMFE React Native拆包及热更新方案 · Solartisan
说到unbundling,官方文档还把 inline requires 一并合起来分析了
看个小例子就很容易明白了
Even without unbundling inline requires can lead to startup time improvements, because the code within VeryExpensive.js will only execute once it is required for the first time
上面的内容主要是关于首屏渲染速度的性能优化
那么进入App后的性能点又在哪里呢?还是回到Bridge
首先,在苹果和谷歌两位大佬的光环下,native代码在设备上的运行速度毋容置疑,而JS作为脚本语言,本来就是以快著称,也就是说两边的独立运行都很快,如此看来,性能瓶颈只会出现在两端的通信上,但两边其实不是直接通信的,而是通过Bridge做中间人,查找、调用模块、接口等操作逻辑,会产生到能让UI层明显可感知的卡顿,那么性能控制就变成了如何尽量减少Bridge所需要的逻辑。
总结起来,核心的RN性能优化点就比较清晰明朗了
每个小点主要会按照容易实施执行的顺序来写
一、是否重新渲染——shouldComponentUpdate
生命周期请看官方文档React.Component - React
react应用中的state和props的改变都会引起re-render
考虑下面这种情况
核心代码是
this.setState({ a: this.state.a })
明明没有改变a,只是setState了一下而已,就直接触发了重新渲染,试想一下,如果页面有大型数据,这会造成多大的性能浪费
加上
shouldComponentUpdate
钩子看看如何嗯,这下好了点,不会无脑渲染了
那么假如是个引用对象呢?
给b永远指向同一个引用对象obj,虽然每次点击的时候,obj.num都会被改变 但是,页面会不会重新渲染呢? 继续看图
很好,对象的内容变了,页面也重新渲染
那么加上
shouldComponentUpdate
比较一下呢?页面毫无变化 原因:b每次都指向了同一个引用对象obj,引用地址没变,
shouldComponentUpdate
只会做浅比较,自然会返回false,页面不会重新渲染到这里应该能很好的解释了
shouldComponentUpdate
的特点那么如何处理引用对象的情况呢?目前最推崇的做法是使用不可变对象immutablejs,facebook自家出的 GitHub - facebook/immutable-js 好了,研究去吧
另外,还有个pureComponent,看下官方介绍就好了 React Top-Level API - React
说到底,也只是会自动使用
shouldComponentUpdate
钩子的普通Component而已,没什么特殊的二、组件响应速度(InteractionManager、requestAnimationFrame、setNativeProps)
1)InteractionManager
InteractionManager
和requestAnimationFrame(fn)
的作用类似,都是为了避免动画卡顿,具体的原因是边渲染边执行动画,或者有大量的code计算阻塞页面进程。InteractionManager.runAfterInteractions
是在动画或者操作结束后执行2)requestAnimationFrame
window.requestAnimationFrame - Web API 接口 | MDN 使用requestAnimationFrame(fn)在下一帧就立即执行回调,这样就可以异步来提高组件的响应速度;
还有
setImmediate/setTimeout()
: 这个是比较原始的奔方法,很有可能影响动画的流畅度3) setNativeProps
Direct Manipulation · React Native 通过Direct Manipulation的方式直接在底层更新了Native组件的属性,从而避免渲染组件结构和同步太多视图变化所带来的大量开销。
这样的确会带来一定的性能提升,同时也会使代码逻辑难以理清,而且并没有解决从JS侧到Native侧的数据同步开销问题。
因此这个方式官方都不再推荐,更推荐的做法是合理使用setState()和shouldComponentUpdate()方法解决这类问题。
三、动画
Animated的前提是尽量减少不必要的动画,具体的使用方式请看官方文档Animated · React Native
如果觉得
Animated
的计算很麻烦,比如一些折叠、增加减少view、改变大小等简单的操作,可以使用LayoutAnimation
来流畅的完成一次性动画 看下直接setState
和使用LayoutAnimation
后的效果对比直接
setState
LayoutAnimation
效果1LayoutAnimation
效果2使用很简单,分为两种情况
componentWillUpdate
钩子里面,让整个组件所有动画都应该该效果,或者在单独需要动画的setState
方法前面使用LayoutAnimation.spring();
(未完待续。。。)
后记
感谢您耐心看到这里,希望有所收获!
如果不是很忙的话,麻烦点个star⭐【Github博客传送门】,举手之劳,却是对作者莫大的鼓励。
我在学习过程中喜欢做记录,分享的是自己在前端之路上的一些积累和思考,希望能跟大家一起交流与进步。