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 相关文章推荐
浏览器脚本兼容 文本框中,回车键触发事件的兼容
Jun 21 Javascript
Javascript中的String对象详谈
Mar 03 Javascript
js使用for循环查询数组中是否存在某个值
Aug 12 Javascript
理解jQuery stop()方法
Nov 21 Javascript
jQuery不使用插件及swf实现无刷新文件上传
Dec 08 Javascript
node.js中的fs.lchownSync方法使用说明
Dec 16 Javascript
JQuery自适应窗口大小导航菜单附源码下载
Sep 01 Javascript
jquery 实现输入邮箱时自动补全下拉提示功能
Oct 04 Javascript
jQuery模拟select实现下拉菜单功能
Jun 20 Javascript
js绑定事件和解绑事件
Apr 27 Javascript
TypeScript高级用法的知识点汇总
Dec 17 Javascript
vue项目中使用eslint+prettier规范与检查代码的方法
Jan 16 Javascript
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实现变色验证码实例
2014/01/06 PHP
PHP代码实现表单数据验证类
2015/07/28 PHP
文件上传之SWFUpload插件(代码)
2015/07/30 PHP
jquery UI 1.72 之datepicker
2009/12/29 Javascript
Jquery练习之表单验证实现代码
2010/12/14 Javascript
javascript中的作用域scope介绍
2010/12/28 Javascript
javascript标签在页面中的位置探讨
2013/04/11 Javascript
JS小功能(操作Table--动态添加删除表格及数据)实现代码
2013/11/28 Javascript
jQuery 1.9.1源码分析系列(十五)之动画处理
2015/12/03 Javascript
AngularJS仿苹果滑屏删除控件
2016/01/18 Javascript
Nodejs全局安装和本地安装的不同之处
2016/07/04 NodeJs
jQuery动态改变多行文本框高度的方法
2016/09/07 Javascript
ajax与json 获取数据并在前台使用简单实例
2017/01/19 Javascript
jquery实现超简单的瀑布流布局【推荐】
2017/03/08 Javascript
Vue.js在使用中的一些注意知识点
2017/04/29 Javascript
使用VueCli3+TypeScript+Vuex一步步构建todoList的方法
2019/07/25 Javascript
vuex存储复杂参数(如对象数组等)刷新数据丢失的解决方法
2019/11/05 Javascript
vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)
2020/04/11 Javascript
微信小程序自定义胶囊样式
2020/12/27 Javascript
[05:26]2014DOTA2西雅图国际邀请赛 iG战队巡礼
2014/07/07 DOTA
Python获取文件ssdeep值的方法
2014/10/05 Python
用Python编写一个每天都在系统下新建一个文件夹的脚本
2015/05/04 Python
python获取指定时间差的时间实例详解
2017/04/11 Python
Python之py2exe打包工具详解
2017/06/14 Python
python中正则表达式与模式匹配
2019/05/07 Python
Python unittest工作原理和使用过程解析
2020/02/24 Python
如何理解Python中的变量
2020/06/01 Python
基于python判断字符串括号是否闭合{}[]()
2020/09/21 Python
39美元购买一副眼镜或太阳镜:39DollarGlasses.com
2018/06/17 全球购物
高中学生干部学习的自我评价
2014/02/21 职场文书
全国税务系统先进集体事迹材料
2014/05/19 职场文书
2015年社区创卫工作总结
2015/04/21 职场文书
小型婚礼主持词
2015/06/30 职场文书
好人好事新闻稿
2015/07/17 职场文书
JS如何实现基于websocket的多端桥接平台
2021/05/14 Javascript
Node.js实现断点续传
2021/06/23 Javascript