1.单个React
组件的性能优化
react
是通过Virtual DOM
来提高渲染性能,虽然每一次页面更新都是对组件的重新渲染,但是并不是将之前渲染的内容全部抛弃重来,而是通过借助Virtual DOM
,计算出对DOM树的最小修改。这就是为什么React
在默认情况下渲染都很快速的原因。
不过,虽然Virtual DOM
能够将每次DOM的修改量减少到最少,但是计算和比较Virtual DOM
依然是一个很复杂的过程。如果能够在开始计算Virtual DOM
之前就可以判断渲染结果不会发生变化,那么就可以直接不要进行Virtual DOM
计算和比较,这样速度就会更快。
react-redux
的shouldComponentUpdate
我们之前介绍过的shouldComponentUpdate
可能是React
组件生命周期函数中除了render
之外最重要的函数了。render
函数决定了‘组件渲染出什么’,而shouldComponentUpdate
函数则决定了‘什么时候不需要重新渲染’。
React
组件类的父类Component
提供了shouldComponentUpdate
的默认实现方式,但是这个默认实现方式只是简单的返回一个true
,也就是说每次更新的时候都要调用所有的生命周期函数,包括调用render
函数,根据render
函数的返回结果计算Virtual DOM
.
回顾一下,使用react-redux
库,我们把完成一个功能的React
组件分为两部分:
- 傻瓜组件:只负责视图部分,处理的是’组件看起来怎么样’的事情,这个傻瓜组件往往用一个函数的无状态组件就足够表示,甚至不需要是一个类的样子,只需要定义一个函数就足够了。
- 容器组件:负责逻辑部分,处理的是’组件如何工作’的事情。这个容器组件有状态,而且保持和
Redux Store
上状态的同步,但是react-redux
的connect
函数把这部分同步的逻辑封装起来了,我们甚至在代码中看不见这个类的样子,往往直接导出connect
返回函数的执行结果就行了。
export default connect(mapStateToProps,mapDispatchToProps)(TodoItem)
虽然代码上不可见,但是connect
的过程中实际上产生了一个无名的React
组件类,这个类定制了shouldComponentUpdate
的实现,实现逻辑是比对这次传递给内存傻瓜组件的props和上一次的props
。如果props
没有变化,那就可以认为渲染结果肯定也一样。
相比React
组件的默认shouldComponentUpdate
函数实现,react-redux
的实现方式当然是前进了一大步。但是在对比prop
和上一次渲染所用的prop
方面。依然用的是尽量简单的方法,做的是‘浅层比较’,即使用javascript
默认的===
来比较。如果prop
的类型是字符串或者数字,只要值相同,那么’浅层比较’也会认为两者相同。但是如果prop
的类型是对象,那么‘浅层比较’只会对比两者是不是同一个对象的引用,如果不是,哪怕这两个对象中的内容完全一样,也会认为是两个不同的prop
。同样,函数类型的prop
也存在这样的问题,要想让它知道两个函数类型的prop
是相同的,就必须让这两个prop
指向同一个函数,如果每次传入的prop
都是一个新创建的函数,就肯定不行了。
2.多个react
组件的性能优化
当一个React
组件被装载,更新和卸载的时候,组件的一系列生命周期函数会被调用。不过,这些生命周期函数是针对一个特定的React
组件的。那么,在一个应用中,从上到下有很多React
组件组合起来,那么他们之间的渲染过程是怎么样的呢?
在装载和卸载阶段没什么性能优化的事情可以做,我们来着重看一下更新阶段:
2.1 React
的调和阶段
首先,什么是调和?React
在更新阶段,通过对比Virtual DOM
的差异,根据不同来修改DOM树,以此来做到最小限度的修改。React
在更新中这个找不同
的过程,就叫做调和。
React
的diff
算法并不复杂,当要对比两个Virtual DOM
的树形结构时,从根节点开始递归往下比对,在树形结构上,每个节点都可以看做一个这个节点以下部分子树的根节点。所以这个diff
算法可以从Virtual DOM
上任何一个节点开始执行。
算法具体原理可以参考另一篇博客:React diff算法浅析
React
首先会检查两个树形的根节点类型是否相同,根据相同或者不同有不同的处理方式。