react组件卸载调用的方法_深入浅出React和Redux(第五章:React组件的性能优化)>锵哥带你读好书系列...

36e3b221ea2bc2a134836c4bc64f0d2c.png

风说:虽然我没有颜色,但我很自由

f20a7ac83600281eb76362af4d8213b7.gif

剧情回顾:

        昨天我们已经学习了如何从一个应用维度去思考一个React应用的构思与设计,这是从0到1的过程,接着我们要开始从1到100的提升过程。

       今天给大家介绍一下,React组件是如何性能优化的,毕竟写出来跟写好是两种不同的境界。

正文:

章节:《深入浅出React和Redux》(第五章:React组件的性能优化)

1.本章会介绍性能优化方法:

A:单个React组件的性能优化

B:多个React组件的性能优化

C:利用reselect提高数据选取的性能

2.React利用Virtual DOM来提高渲染性能,虽然每一次页面更新都是对组件的重新渲染,但是并不是将之前渲染的内容全部抛弃重来,借助Virtual DOM,React能够计算出对DOM树的最小修改,这就是React默认情况下渲染都很迅捷的秘诀

3.React Pref的使用方法,请查看书本106页,主要就是

A:打开开发者工具,切换到Pref,确保wasted选项被勾选

B:点击React Pref工具左侧的Start按钮,开始性能测量,这时按钮会变成Stop按钮

C:然后勾选Todo应用的First那一项,使它变成完成状态

D:点击React Pref工具左侧的Strop按钮,结束性能测量,界面上会显示测量结果。

4.在React Pref工具中,可以看见发现浪费的渲染过程,React Pref工具记录在点击Start按钮和Stop按钮之间的所有的React渲染,如果有组件计算Virtual DOM之后发现和之前的Virtual DOM相同,那就认为是一次浪费。注意,这里说的浪费是计算Virtual DOM的浪费,并不是访问DOM树的浪费

5.我们应该忘记忽略很小的性能优化,可以说97%的情况下,过早的优化是万恶之源,而我们应该关心对性能影响最关键的那另外3%的代码

6.所谓的“过早的优化”,指的是没有任何量化证据情况下开发者对性能优化的猜测,没有可测量的性能指标,就完全不知道当前的性能瓶颈在何处,完成优化之后也无法知道性能优化是否达到了期望的目标

7.虽然代码上不可见,但是connect的过程中实际上产生了一个无名的React组件类,这个类定制了shouldComponentUpdate函数的实现

8.只要Redux Store上对应state没有改变,Foo就不会经历无意义的Virtual DOM产生和比对过程,也就避免了浪费

9.同样的方法也可以应用在TodoItem组件上。不过因为TodoItem没有直接从Redux Store上读取状态,但我们依然可以使用react-redux方法,只是connect函数的调用不需要任何参数,要做的只是将定义TodoItem组件的代码最后一行改成如下代码,代码如下:

export default connect()(TodoItem);

10.在上面的例子中,在connect函数的调用没有参数,没有mapStateToProps和mapDispatchToProps函数,使用connect来包裹TodoItem的唯一目的就是利用那个聪明的shouldComponentUpdate函数

11.总之,要想让react-redux认为前后的对象类型prop是相同的,就必须要保证prop是指向同一个JavaScript对象

12.同样的情况也存在于函数类型的prop,react-redux无从知道两个不同的函数是不是做着一样的事情,要想让它认为两个prop是相同的,就必须让这两个prop指向同样一个函数,如果每次传入prop的都是一个新创建的函数,那肯定就没法让prop指向同一个函数了

13.对比两种方式,可以看到无论如何TodoItem都需要使用react-redux,都需要定义产生定制prop的mapDispatchToProps,都要求TodoList传入一个id,区别只在于actions是由父组件导入还是由组件自己导入。

14.我们现在考虑的不是单个React组件内的渲染过程,而是多个React组件之间组合的渲染过程。和单个React组件的生命周期一样,React组件也要考虑三个阶段:装载阶段、更新阶段和卸载阶段

15.在装载过程中,React通过render方法在内存中产生了一个树形的结构,树上每一个节点代表一个React组件或者原生的DOM元素,这个树形结构就是所谓的Virtual DOM,React根据这个Virtual DOM来渲染产生浏览器中的DOM树

16.实际上,React在更新阶段很巧妙地对比原有的Virtual DOM和新生成的Virtual DOM,找出两者的不同之处,根据不同来修改DOM树,这样只需做最小的必要改动。React在更新中这个“找不同”的过程,就叫做Reconciliation(调和)

17.React实际采用的算法需要的时间复杂度是O(N),因为对比两个树形怎么着都要对比两个树形上的节点,似乎也不可能有比O(N)时间复杂度更低的算法。

18.其实React的Reconciliation算法并不复杂,当React要对比两个Virtual DOM的树形结构的时候,从根节点开始递归往下比对,在树形结构上,每个节点都可以看作一个这个节点以下部分子树的根节点。所以其实这个对比算法可以从Virtual DOM上任何一个节点开始执行。

19.如果树形结构根节点类型不相同,那就意味着改动太大了,也不要去费心考虑是不是原来那个树形的根节点被移动到其他地方去了,直接认为原来那个树形结构已经没用,可以扔掉,需要重新构建新的DOM树,原来的树形上的React组件会经历“卸载”的生命周期

20.虽然是浪费,但是为了避免O(N³)的时间复杂度,React必须要选择一个更简单更快捷的算法,也就只能采用这种方式。

21.作为开发者,很显然一定要避免上面这样浪费的情景出现。所以,一定要避免作为包裹功能的节点类型被随意改变,像上面的例子中,把div换成span只会带来没有必要的组件重新装载

22.如果两个树形结构的根节点类型相同,React就认为原来的根节点只需要更新过程,不会将其卸载,也不会引发根节点的重新装载。

23.这时,有必要区分一下节点的类型,节点的类型可以分为两类:一类是DOM元素类型,对应的就是HTML直接支持的元素类型,比如div,span和p;另一类是React组件,也就是利用React库定制的类型。

24.React不会使用一个O(N²)时间复杂度的算法去找出前后两列子组件的差别,默认情况下,在Reat的眼里,确定每一个组件在组件序列中的唯一标识就是它的位置,所以他也完全不懂哪些子组件实际上并没有改变,为了让React更加“聪明”,就需要开发者 提供一点帮助

25.如果在代码中明确地告诉React每个组件的唯一标识,就可以帮助React在处理这个问题时聪明很多,告诉React每个组件“身份证”的途径就是key属性。

26.理解了key属性的作用,也就知道,在一列子组件中,每个子组件的key值必须唯一,不然就没有帮助React区分各个组件的身份,这并不是一个很难的问题,一般很容易给每个组件找到一个唯一的id。

27.但是这个key值只是唯一还不足够,这个key值还需要是稳定不变的,试想,如果key值虽然能够在每个时刻都唯一,但是变来变去,那么就会误导React做出错误判断,甚至导致错误的渲染结果。

28.如果通过数组来产生一组子组件,一个常见的错误就是将元素在数组中的下标值作为key,这么做非常危险,因为,假如没有使用key的话React会在运行时输出一个错误提示,但是错误地使用key值React就不会给出错误提示了,因为React无法发现开发者的错误。

29用数组下标作为key,看起来key值是唯一的,但是却不是稳定不变的,随着todos数组值的不同,同样一个TodoItem实例在不同的更新过程中在数组中的下标完全可能不同,把下标当做key就让React彻底乱套了。

30.在前面的例子中,都是通过优化渲染过程来提高性能,既然React和Redux都是通过数据驱动渲染过程,那么除了优化渲染过程,获取数据的过程也是一个需要考虑的优化点。可以使用reselect库来提高数据获取性能。

31.reselect提供了创造选择器的createSelector函数,这是一个高阶函数,也就是接受函数为参数来产生一个新函数的函数。

32.Redux要求每一个reducer不能修改state状态,如果要返回一个新的状态,就必须返回一个新的对象。如此一来,Redux Store状态树上某个节点如果没有改变,那么我们就有信心这节点下数据没有变化,应用在reselect中,步骤一的运算就可以确定直接缓存运算结果。

33.虽然reselect的createSelector创造的选择器并不是一个纯函数,但是createSelector接受的所有函数参数都是纯函数,虽然选择器有“记忆”这个副作用,但是只要输入参数state没有变化,产生的结果也就没有变化,表现得却类似于纯函数。

34.只要Redux Store状态树上的filter和todos字段不变,无论怎样触发TodoList的渲染过程,都不会引发没有必要的便利todos字段的运算,性能自然更快。

35.虽然reselect库以re开头,但是逻辑上和React/Redux没有直接关系。实际上,在任何需要这种具有记忆的计算场合都可以使用reselect,不过,对于React和Redux组合的应用,reselect无疑能够提供绝佳的支持。

36.对比反范式方式和范式方式的优劣,不难看出范式方式更合理。因为虽然join数据需要花费计算时间,但是应用了reselect之后,大部分情况下都会命中缓存,实际上也就没有花费很多计算时间了。

37.我们了解了利用react-redux提供的shouldComponenetUpdate实现来提高组件渲染功能的方法,一个要诀就是要避免传递给其他组件的prop值是一个不同的对象,不然会造成无谓的重复渲染。

观后感回放:

粉丝路人甲:“我感觉自己开始吃成长快乐了耶”

锵哥:“从陌生到熟练,唯有坚持”

粉丝路人甲:“嗯,好”

锵哥:“加油!”

粉丝路人甲:“04d7f94bded35bc95e3568743bea04c9.png04d7f94bded35bc95e3568743bea04c9.png04d7f94bded35bc95e3568743bea04c9.png

广告:

本人从事全栈工程师,目前主要工作能力涵盖的范围有:android,ios,h5,pcWeb,react,vue,node,java服务端,微信服务号,微信小程序,支付宝生活号,支付宝小程序。

本公众号会不定期的将自己的研发感悟,以及心得笔记无私奉献给大家。还等啥,赶快上车吧,铁子们!!!😁(还会有其他的福利哦!快来吧)

官方订阅号:锵哥的觉悟

微信号:DY_suixincq

二维码:

b825f8761ac53f70baba798d41f9723f.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值