如果使用工具检测出页面浪费的渲染次数太多,就需要检查代码是否写法上有问题了。虽然 Virtual DOM
算法可以避免大多无效的真实 DOM
操作,但还是会浪费时间在计算不会改变的虚拟 DOM
上,也就是执行了 render
函数,但发现并没有任何改变。
转载https://wulv.site/2017-07-02/react-perf-code.html
key
key
属性作为列表的子组件的身份标识,是不能重复的。不写 key
属性, React
会在浏览器控制台报 warning
,有时候我们为了去除这个警告,直接使用数组的索引做 key
,这是不太好的。
|
|
如果 list
发生变化,比如从中间去除一条数据,两次渲染之间 key
相同的子组件,React
认为他们是同一个组件,这样很多个 Child
组件都改变了。最好使用一个唯一的value.id
,如果没有 id
,使用value.name
之类的也是可以的, key
不必须是数字,只要保证不重复的字符就可以了。
shouldComponentUpdate
在 React
生命周期里, shouldComponentUpdate
表示组件是否需要被更新,我们可以做一些判断,来优化组件性能,让一些确定不会改变视图的操作,直接不去计算。shouldComponentUpdate
在初始化时或者使用 forceUpdate
时是不被执行的。 shouldComponentUpdate
默认返回值是 true
,也就是组件一定会更新,如果返回值为 false
,会阻止后面 componentWillUpdate
、 render
、 componentDidUpdate
等操作。并且如果 componentWillReceiveProps
里有 setState
操作也会被阻止。
比如我们确定如果一个子组件的 name
和 tel
属性不变,子组件就不需要更新:
|
|
我们最好只传递 component
需要的 props
,如果传得太多,或者层次传得太深,都会加重 shouldComponentUpdate
里面的数据比较负担,因此,也请慎用<Component {...this.props} />
操作。
|
|
PureComponent
PureComponent
是一个很有效的优化,在前面的博客专门有一篇来介绍:React PureComponent 使用指南。
Stateless components
React v0.14
就添加了 functional components
,他的行为很像只有一个 render
方法的 class components
,但没有生命周期方法,也没有实例对象,所以不能够使用 ref
。目前来说 functional components
并没有特别优化,在内部也是包装在同一个类中。我们可以直接以函数方式使用,这样可以优化一部分性能:
|
|
React
官方已经承诺,在未来会通过避免不必要的检查和内存分配来对这些组件进行性能优化。
constant elements
我们可以将确定不变的html
代码抽离出来直接当做一个变量,写在jsx
里,会解析jsx
语法,生成React.createElement()
代码。如果提升成静态元素,jsx
会直接把它们当做一个值,减少了解析过程:
|
|
这个操作其实有babel
插件babel-react-optimize,可以自动帮我们提取。
慎用setState
如果是和视图无关的,但有变化的数据,不要放在 state
里面,比如某个组件需要 mouseDown
时标记开始记录, mouseUp
清除记录,最好直接当做实例的一个属性。这样可以避免执行无效的 render
操作。
|
|
总结一下:render
函数里面要用到的东西放props/state
(影响 view
),其他的不要放进去,写成模块内的私有变量(跨实例共享)或者组件实例上的变量。
在React PureComponent
使用指南里,复杂状态与简单状态不要共用一个组件那一段也提到了慎用setState
的原因。
慎用bind
Component
的 render
里不使用 bind
绑定 this
,可以放在 constructor
里绑定好,或者直接使用箭头函数,如果要动态传参,可以使用闭包,或者可以直接把处理函数传入子组件,子组建时可以拿到参数,再执行父组件的处理函数就可以了。
|
|
如果每次都在 render
里面的 jsx
去 bind
这个方法,会消耗性能,因为每次bind
都会返回一个新函数,重复创建静态函数肯定是不合适的(闭包也是这样,但bind
内部有一系列的算法,比闭包复杂多了)。
关于bind
性能问题可以查看以下资料:
总的来说,目前浏览器已经足够快了,在bind
没有成为性能瓶颈之前,这都只是代码写法上的事。