默认的渲染行为
初始情况下,组件树中所有组件都进行了虚拟dom的生成(绿色)
接着组件进行setState(红色节点),然后重新生成虚拟dom(蓝色节点),生成的虚拟dom树再与之前的对应节点的虚拟dom进行diff,然后对差异进行应用
针对以上过程可以发现,假如setState没有影响到子组件,或者只影响到了一个子组件(通过props传递了state),则另外没有受影响的子组件的虚拟dom生成是无用的(因为子组件不需要发生变化)。对于这部分虚拟dom的生成以及对比,是无用功。
渲染优化
针对以上问题,如何在不需要dom更新的时候,避免虚拟dom的比较呢?
只需要在shouldComponentUpdate(nextProps,nextState)中判断state和props,如果没有发生变化,则返回false即可(这个函数默认实现是直接返回true),返回true代表重新生成虚拟dom,新旧虚拟dom进行对比,然后局部刷新;返回false,不刷新,没有刚才这一系列步骤,具体可以去查看生命周期,返回true会少执行多个方法。
假如以下浅蓝色节点设置了state和props的判断(相同返回false),则当红色节点setState的时候,只会生成一颗包含了两个虚拟dom节点的树(蓝色),diff的时候也仅仅针对这两个节点进行diff,效率得到提升。
基本的做法是在这个函数中,检查state和props有没有发生变更,用当前的状态与新状态(nextXXX)进行深比较:
shouldComponentUpdate: function (nextProps, nextState) { return !isDeepEqual(this.props,nextProps) || !isDeepEqual(this.state,nextState); }
但是深度比较消耗资源,官方推荐使用immutable,这样一来每次变动,都会创建新的对象,两者进行 == 比较(浅比较,shallow equal)即可,而不再需要深比较了。
使用Immutable
在react class中的getInitialState中返回一个Immutable数据(或者在constructor中把state初始化,设置immutable数据),然后其他地方通过state.xx获取到immutable数据,进行修改。把修改后的值重新setState回去。
PureRenderMixin:会自行为组件添加shouldComponentUpdate,实现对props和state的浅比较。但这个不能直接拿来使用,因为是浅比较。这就必须配合上immutable来使用了。
PureComponent:react15中用来替代PureRenderMixin,两者有一样的功能。
Redux的默认性能优化
官方有以下两个规定,违背这两个规定会导致redux的默认性能优化不起作用:
1. 容器型组件必须为Pure Component,即组件只依赖于state和props 2. 全局状态树(global state)的任何变动都是immutable的
通过connect生成的容器中,就默认提供了shouldComponentUpdate函数,其中对props和state进行了浅比较。
列表元素的key值
当渲染一个列表的时候,列表元素标签上没有key属性,会出现一个警告
a key should be provided for list items
元素的key值在兄弟元素之间应该是唯一的,但不需要全局唯一。
使用key的好处是利于dom的复用,查看:http://www.cnblogs.com/hellohello/p/7988346.html
误区
不要在render中创建新对象(或函数),然后模板中引用这个新对象:
render() { const {items} = this.props; const newData = { hello: 'world' }; return <Item name={name} data={newData} /> }
这样在Item的内部shouldComponentUpdate中判断props时总会不一致,会令Item每次都进行渲染
解决办法是:转化为满足 === 判断的数据,再传递。