React如何避免重渲染


Posted in Javascript onApril 10, 2018

组件的重新渲染

我们可以在 React 组件中的 props 和 state 存放任何类型的数据,通过改变 props 和 state,去控制整个组件的状态。当 props 和 state 发生变化时,React 会重新渲染整个组件,组件重新渲染的过程可简化如下图:

React如何避免重渲染

译者之前对diff的理解是,对于一个改变 props 的组件,diff能自动计算出组件内部DOM树的不同,然后经过对比,找出真正变化的DOM节点,对变化部分进行渲染。这个是错误的理解,diff算法只是用来计算出改变状态或 props的组件/虚拟节点,而这个组件/虚拟节点,无论多大,它都会重新渲染。

假设有一个渲染完成的组件,如下图:

React如何避免重渲染

接下来因为状态改变,需要重新渲染下图的绿色的节点,如下图:

React如何避免重渲染

一般的想法是只需要更新下面的三个绿色节点就能够完成组件的更新

React如何避免重渲染

然而!只要组件的 props 或 state 发生了变化就会重新渲染整个组件,因此除了上述的三个绿色节点以外,还需要重新渲染所有的黄色的节点

React如何避免重渲染

除了必要渲染的三个节点外,还渲染了其他不必要渲染的节点,这对性能是一个很大的浪费。如果对于复杂的页面,这将导致页面的整体体验效果非常差。因此要提高组件的性能,就应该想尽一切方法减少不必要的渲染。

shouldComponentUpdate

shouldComponentUpdate这个函数会在组件重新渲染之前调用,函数的返回值确定了组件是否需要重新渲染。函数默认的返回值是 true,意思就是只要组件的 props 或者 state 发生了变化,就会重新构建 virtual DOM,然后使用 diff 算法进行比较,再接着根据比较结果决定是否重新渲染整个组件。函数的返回值为 false 表示不需要重新渲染。

函数默认返回为 true.

PureRenderMixin

React 官方提供了 PureRenderMixin 插件,插件的功能就是在不必要的情况下让函数 shouldComponentUpdate 返回 false, 使用这个插件就能够减少不必要的重新渲染,得到一定程度上的性能提升,其使用方法如下:

import PureRenderMixin from 'react-addons-pure-render-mixin';
  class FooComponent extends React.Component {
   constructor(props) {
    super(props);
    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
   }

   render() {
    return <div className={this.props.className}>foo</div>;
   }
  }

我们需要在组件中重写 shouldComponentUpdate,PureRenderMixin源码中对PureRenderMixin.shouldComponentUpdate的定义是这样

shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
  }

重写的方法里面根据组件的目前的状态和组件接下来的状态进行浅比较,如果组件的状态发生变化则返回结果为 false,状态没有发生变化则返回结果为 true

shouldComponentUpdate(nextProps, nextState) {
    return !shallowEqual(this.props, nextProps) ||
        !shallowEqual(this.state, nextState);
  }

在 React 的最新版本里面,提供了 React.PureComponent 的基础类,而不需要使用这个插件。

译者注:所以在一个较大的组件决定重渲染的时候,我们可以在每一个子组件中绑定新的shouldComponentUpdate方法,这样可以减少子组件重新渲染的次数。

我们自己可以重写 shouldComponentUpdate 这个函数,使得其能够对任何事物进行比较,也就是深比较(通过一层一层的递归进行比较),深比较是很耗时的,一般不推荐这么干,因为要保证比较所花的时间少于重新渲染的整个组件所花的时间,同时为了减少比较所花的时间我们应该保证 props 和 state 尽量简单,不要把不必要的属性放入 state,能够由其他属性计算出来的属性也不要放入 state 中。

Immutable.js

对于复杂的数据的比较是非常耗时的,而且可能无法比较,通过使用 Immutable.js 能够很好地解决这个问题,Immutable.js 的基本原则是对于不变的对象返回相同的引用,而对于变化的对象,返回新的引用。因此对于状态的比较只需要使用如下代码即可:

shouldComponentUpdate() {
    return ref1 !== ref2;
  }

同样需要我们在子组件中将shouldComponentUpdate方法重写。

Pure Component

如果一个组件只和 props 和 state 有关系,给定相同的 props 和 state 就会渲染出相同的结果,那么这个组件就叫做纯组件,换一句话说纯组件只依赖于组件的 props 和 state,下面的代码表示的就是一个纯组件。

render() {
     return (
       <div style={{width: this.props.width}}>
           {this.state.rows}
       </div>
     );
  }

如果某个子组件的 props 是固定的不会发生变化,我们叫做无状态组件。在这个组件里面使用 pureRenderMixin 插件,能够保证 shouldComponentUpdate 的返回一直为 false。所以,分清纯组件和无状态组件,在无状态组件中重写shouldComponentUpdate方法是最好的选择。

key

在写动态子组件的时候,如果没有给动态子项添加key prop,则会报一个警告。这个警告指的是,如果每一个子组件是一个数组或者迭代器的话,那么必须有一个唯一的key prop,那么这个key prop是做什么的呢?
我们想象一下,假如需要渲染一个有5000项的成绩排名榜单,而且每隔几秒就会更新一次排名,其中大部分排名只是位置变了,还有少部分是完全更新了,这时候key就发挥作用了,它是用来标识当前的唯一性的props。现在尝试来描述这一场景

[{
   sid: '10001',
   name: 'sysuzhyupeng'
  }, {
   sid: '10008',
   name: 'zhyupeng'
  }, {
   sid: '120000',
   name: 'yupeng'
  }]

其中sid是学号,那么我们来实现成绩排名的榜单

import React from 'react';
  function Rank({ list }){
    return (
     <ul>
       {list.map((entry, index)=>(
         <li key={index}>{entry.name}</li>
       ))}
     </ul>
    )
  }

我们把key设成了序号,这么做的确不会报警告了,但这样是非常低效的做法,这个key是用来做virtual Dom diff的,上面的做法相当于用了一个随机键,那么不论有没有相同的项,更新都会重新渲染。

正确的做法非常简单,只需要把key的内容换成sid就可以了。

那么还有另一个问题,当key相同的时候,React会怎么渲染呢,答案是只渲染第一个相同key的项,且会报一个警告。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 获取(接收)地址栏参数值的方法
Apr 01 Javascript
纯css+js写的一个简单的tab标签页带样式
Jan 28 Javascript
jQuery ui 利用 datepicker插件实现开始日期(minDate)和结束日期(maxDate)
May 22 Javascript
手机端网页点击链接触发自动拨打或保存电话的示例代码
Aug 15 Javascript
使用AngularJS实现可伸缩的页面切换的方法
Jun 19 Javascript
详解JavaScript中数组的相关知识
Jul 29 Javascript
基于HTML模板和JSON数据的JavaScript交互(移动端)
Apr 06 Javascript
vue 每次渲染完页面后div的滚动条保持在最底部的方法
Mar 17 Javascript
小程序实现列表多个批量倒计时
Jan 29 Javascript
基于vue实现滚动条滚动到指定位置对应位置数字进行tween特效
Apr 18 Javascript
如何使用 vue-cli 创建模板项目
Nov 19 Vue.js
Vue中的nextTick作用和几个简单的使用场景
Jan 25 Vue.js
vue select组件的使用与禁用实现代码
Apr 10 #Javascript
vue 自定义 select内置组件
Apr 10 #Javascript
JavaScript如何对图片进行黑白化
Apr 10 #Javascript
axios 处理 302 状态码的解决方法
Apr 10 #Javascript
vue.js中npm安装教程图解
Apr 10 #Javascript
vue实现验证码按钮倒计时功能
Apr 10 #Javascript
vue 微信授权登录解决方案
Apr 10 #Javascript
You might like
PHP中uploaded_files函数使用方法详解
2011/03/09 PHP
浅析php中三个等号(===)和两个等号(==)的区别
2013/08/06 PHP
php中的curl使用入门教程和常见用法实例
2014/04/10 PHP
PHP中的替代语法简介
2014/08/22 PHP
laravel框架之数据库查出来的对象实现转化为数组
2019/10/23 PHP
Add Formatted Data to a Spreadsheet
2007/06/12 Javascript
javascript 获取页面的高度及滚动条的位置的代码
2010/05/06 Javascript
js post方式传递提交的实现代码
2010/05/31 Javascript
分享27款非常棒的jQuery 表单插件
2011/03/28 Javascript
suggestion开发小结以及对键盘事件的总结(针对中文输入法状态)
2011/12/20 Javascript
Javascript加载速度慢的解决方案
2014/03/11 Javascript
jQuery插件HighCharts绘制简单2D折线图效果示例【附demo源码】
2017/03/21 jQuery
Vuex之理解Store的用法
2017/04/19 Javascript
javaScript canvas实现(画笔大小 颜色 橡皮的实例)
2017/11/28 Javascript
微信小程序ajax实现请求服务器数据及模版遍历数据功能示例
2017/12/15 Javascript
解决layer弹出层的内容页点击按钮跳转到新的页面问题
2019/09/14 Javascript
Vue 中获取当前时间并实时刷新的实现代码
2020/05/12 Javascript
javascript中闭包closure的深入讲解
2021/03/03 Javascript
python比较2个xml内容的方法
2015/05/11 Python
python中Switch/Case实现的示例代码
2017/11/09 Python
Python Grid使用和布局详解
2018/06/30 Python
python实现发送form-data数据的方法详解
2019/09/27 Python
Python3 把一个列表按指定数目分成多个列表的方式
2019/12/25 Python
基于Numba提高python运行效率过程解析
2020/03/02 Python
携程旅行网:中国领先的在线旅行服务公司
2017/02/17 全球购物
Loreto Gallo英国:欧洲领先的在线药房
2021/01/21 全球购物
C/C++程序员常见面试题二
2015/11/19 面试题
上海某公司.net方向笔试题
2014/09/14 面试题
一些Solaris面试题
2013/03/22 面试题
最新结婚典礼主持词
2014/03/14 职场文书
法定授权委托证明书
2014/09/27 职场文书
入党积极分子十八届四中全会思想汇报
2014/10/23 职场文书
2014年信用社工作总结
2014/11/25 职场文书
2015年乡镇流动人口工作总结
2015/05/12 职场文书
JavaScript ES6的函数拓展
2022/01/18 Javascript
redis数据一致性的实现示例
2022/03/18 Redis