React性能优化

React性能优化

单个React组件的性能优化

shouldComponentUpdate

React利用Virtual DOM来提高渲染性能,但是Virtual DOM计算前后的区别仍然需要消耗时间。如果我们已经提前知道一个组件不应该更新,那么直接通过shouldComponentUpdate函数返回false,组件则不会进行接下来的update操作,也就不需要进行Virtual DOM的计算,可以节省很长的时间。

但是,做这种优化本来就需要额外的时间成本,那我们应该如果去决定做哪些组件的优化呢?一个是使用React Perf插件来进行组件的性能测量,找到性能瓶颈,另外一个就是对一些用的很多的组件进行性能优化,如果长列表的每个子组件等。

pureRender

pure render的实现是重写shouldComponentUpdate函数。引入pure render后,组件在调用shouldComponentUpdate函数时,会对props和state做浅比较,然后根据浅比较结果返回true或false,决定是否render。pure render的使用,可以从一定程度上提高渲染性能。由于pure render是做浅比较来决定是否更新,所以需要务必保证props和state数据的扁平化结构,数据尽量不使用引用类型数据。

无状态组件

此外,还可以考虑将无状态组件写成函数形式,这样组件本身仅仅是一个函数,就省去了React标准组件生命周期函数执行等步骤,也可以大大提高组件的渲染效率。

多个React将组件的性能优化

React组件的挂载和卸载过程都是必须要经历的过程,可以使用上面的无状态组件来做优化,其他就没有很多的优化点。组件的优化更多的是关注更新的过程。

React的调和(Reconciliation)过程

React的Reconciliation算法是用来更新React组件。当React要对比两个Virtual DOM的树形结构时,从根节点开始递归往下比对,然后根据节点类型做相应的操作。这种做法是性能和复杂度的最好折衷,该算法复杂度为O(N)

节点类型不同

如果树形结构的根节点类型不同,那就意味着改动太大,React会认为没有必要再做对比,会直接删除原有的树形结构,原有的组件会执行卸载过程,而新的组件会被添加到树形结构中,执行挂载过程。

<div>
    <Todos />
</div>
<span>
    <Todos />
</span>

如上面的两个组件,React组件在对比前后Virtual DOM时,会认为div和span类型不会,就直接会卸载上面的组件,然后装载下面的组件。这样会造成巨大的浪费。所以,在开发时,一定要避免作为包裹功能的节点类型的随意改变。

节点类型相同

节点类型相同时,React会进行更新过程,不会引发根节点的重新装载。在React中,节点类型有两种:DOM元素类型、React组件。对于DOM元素类型,React会保留节点对应的DOM元素,只对树形结构根节点上的属性和内容做一下对比,修改不同的部分。对于React组件,React会根据新节点props去更新原来根节点的组件实例,引发这个组件实例的更新过程,即一系列的更新过程生命周期函数。

在组件更新过程中,如果可以提前判断组件是否应该更新,在shouldComponentUpdate函数中提前返回false,会是一个性能优化方案。

多个子组件

<ul>
    <TodoItem text="one" />
    <TodoItem text="two" />
</ul>

假设有上面一个类型的列表,要在最上面加一项,得到下方的结构。

<ul>
    <TodoItem text="zero" />
    <TodoItem text="one" />
    <TodoItem text="two" />
</ul>

React组件会从上往下比较,会认为新结构中zero组件是旧结构中one组件的更新,认为新结构中one组件时旧结构中two组件的更新,而新结构中two组件则是新结构,要进行加载。要避免类似上述情形的浪费,需要给列表中的每一项制定一个唯一且稳定不变的key值(每个组件的唯一标识),这样,React就可以根据key值去区分同一类型的组件,避免出现上述的情形。

<ul>
{
    todos.map((item, index)) => (
        <TodoItem key={index} text={item.text} />
    )
}
</ul>

如上图的key的赋值方式便是错误的,因为不符合key值即唯一又稳定不变的要求。这种赋值方式仍然不能避免上面的情形的浪费,所以,一定要注意。正确的赋值方式可以如下:

<ul>
{
    todos.map((item)) => (
        <TodoItem key={item.id} text={item.text} />
    )
}
</ul>

数据性能优化

React和Redux是由数据驱动的,优化组件渲染是一大部分,而数据方面的性能优化也同样重要。

reselect

reselect库的工作原理:只要相关状态没有改变,那就直接使用上一次的缓存结果。reselect是利用了缓存计算结果的方式,避免重复的大量结算,适用于要进行大量运算且重复度较高的场景。

Immutable

在JavaScript中,无法通过 === 来判断两个对象是否相同,要判断两个对象是否相同需要做深比较,但是这样往往造成性能浪费。immutable表示不可变,它提供了一种存储方案,可以在使用了其库后,可以直接通过immutable来判断两个对象是否相等。

如果不使用immutable,要使用shouldComponentUpdate的话,需要对前后props和state做深比较,而使用后,直接通过 === 符号判断前后数据是否相等即可。

其他

直出

直出是目前常用的优化方案之一。一般情况下,页面需要先加载html,然后再加载js文件、css文件,这样的加载过程消耗巨大,所以React提供了一种服务器渲染的方案,即直出方案。React提供了服务器渲染的功能,即可以在服务器端就渲染后相应的DOM结构,用户拉取html后可以直接看到最终页面,节省了js、css文件的加载和渲染时间。相应的,服务器的压力也就变大了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏河湾欢儿的专栏

5.规范<1>

规范: 在一个项目中开始的时候,每个人都有自己的习惯与编码规范,在项目进行的过程中有些人会离职,那他的风格会在代码中体验,以后再来新人的时候,还得适应,这样代...

1103
来自专栏HT

基于HTML5 Canvas 实现矢量工控风机叶轮旋转

之前在拓扑上的应用都是些静态的图元,今天我们将在拓扑上设计一个会动的图元——叶轮旋转。 先看看最后我们实现的效果:http://www.hightopo.com...

2188
来自专栏彭湖湾的编程世界

redux-form的学习笔记二--实现表单的同步验证

(注:这篇博客参考自redux-form的官方英文文档)左转http://redux-form.com/6.5.0/examples/syncValidatio...

3015
来自专栏web前端-

JQuery事件

       focus()                 ------获得焦点事件

1074
来自专栏超然的博客

前端基础精简总结

ES5: String、Number、Boolean、Null、Undefined、Object ES6增: Symbol 其中,object为引用,其...

1344
来自专栏前端侠2.0

table锁定列的一个方法.及琢磨思路

这是从http://stackoverflow.com找到一个一链接,删除多余的css,加上色就是上图。

1602
来自专栏web前端教室

重学javascript 红皮高程

最近的先行者计划的学习内容是JS高级程序设计,正好我也跟着再重过一遍JS基础的内容。 这行做到现在,我用JQ,写一般的应用,业务逻辑,插件、组件,只要逻辑清晰写...

1908
来自专栏finleyMa

Chrome 功能总结

原文:https://developers.google.com/web/updates/2017/08/devtools-release-notes#awai...

1212
来自专栏python3

tkinter -- tkCommonDialog

各个 参数的意义都 一样,只是 ok 的返回值为保存的文件名称;如果取消则为 None

851
来自专栏HT

基于HT for Web矢量实现2D叶轮旋转

之前在拓扑上的应用都是些静态的图元,今天我们将在拓扑上设计一个会动的图元——叶轮旋转。 我们先来看下这个叶轮模型长什么样 ? 从模型上看,这个叶轮模型有三个叶片...

1915

扫码关注云+社区