reactjs性能优化之shouldComponentUpdate

2023-10-27

更多内容请关注:

性能优化

每当开发者选择将react用在真实项目中时都会先问一个问题:使用react是否会让项目速度更快,更灵活,更容易维护。此外每次状态数据发生改变时都会进行重新渲染界面的处理做法会不会造成性能瓶颈?而在react内部则是通过使用一些精妙的技巧来最小化每次造成ui更新的昂贵的dom操作从而保证性能的。

避免直接作用于DOM

react实现了一层虚拟dom,它用来映射浏览器的原生dom树。通过这一层虚拟的dom,可以让react避免直接操作dom,因为直接操作浏览器dom的速度要远低于操作javascript对象。每当组件的属性或者状态发生改变时,react会在内存中构造一个新的虚拟dom与原先老的进行对比,用来判断是否需要更新浏览器的dom树,这样就尽可能的优化了渲染dom的性能损耗。

在此之上,react提供了组件生命周期函数,shouldComponentUpdate,组件在决定重新渲染(虚拟dom比对完毕生成最终的dom后)之前会调用该函数,该函数将是否重新渲染的权限交给了开发者,该函数默认直接返回true,表示默认直接出发dom更新:

shouldComponentUpdate: function(nextProps, nextState) {
      return true;
}

值得注意的是,react会非常频繁的调用该函数,所以如果你打算自己实现该函数的逻辑,请尽可能保证性能。

比方说,你有一个拥有多个帖子的聊天应用,如果此时只有一个发生了变化,如果你如下实现了shouldComponentUpdate,react会根据情况避免重新渲染那些没有发生变化的帖子:

shouldComponentUpdate: function(nextProps, nextState) {
      // TODO: return whether or not current chat thread is different to former one.
      // 根据实际情况判断当前帖子的状态是否和之前不同
}

总之,react尽可能的避免了昂贵的dom操作,并且允许开发者干涉该行为。

注意:如果你使用了Redux的话,那么它帮你做了一些初级的浅对比来帮助我们判断是否需要重新render

shouldComponentUpdate实战

这里举个包含子元素的组件例子,如下图:

图中每个圆点表示一个dom节点,当某个dom节点的shouldComponentUpdate返回false时(例如c2),react就无需为其更新dom,注意,react甚至根本不会去调用c4和c5节点的shouldComponentUpdate函数哦~

图中c1和c3的shouldComponentUpdate返回了true,因此react会检查检查其它们包含的直接子节点。最有趣的是c8节点,虽然调用它的shouldComponentUpdate方法返回的是true,但react检查后发现其dom结构并未发生改变,所以react最终是不会重新渲染其浏览器dom的。

上图的情况下,react最终只会重新渲染c6,原因你应该懂的。

那么我们应该如何实现shouldComponentUpdate函数呢?假设你有一个只包含字符串的组件,如下:

React.createClass({
      propTypes: {
        value: React.PropTypes.string.isRequired
      },

      render: function() {
        return <div>{this.props.value}</div>;
      }
});

我们可以简单的直接实现shouldComponentUpdate如下:

shouldComponentUpdate: function(nextProps, nextState) {
      return this.props.value !== nextProps.value;
}

目前为止一切都很顺利,处理基础类型的属性和状态是很简单的,我们可以直接使用js语言提供的===比对来实现一个mix并注入到所有组件中,事实上,react自身已经提供了一个类似的:PureRenderMixin

但是如果你的组件所拥有的属性或状态不是基础类型呢,而是复合类型呢?比方说是一个js对象,{foo: 'bar'}

React.createClass({
      propTypes: {
        value: React.PropTypes.object.isRequired
      },

      render: function() {
        return <div>{this.props.value.foo}</div>;
      }
});

这种情况下我们刚才实现的那种shouldComponentUpdate就歇菜了:

// 假设 this.props.value 是 { foo: 'bar' }
// 假设 nextProps.value 是 { foo: 'bar' },
// 但是nextProps和this.props对应的引用不相同
this.props.value !== nextProps.value; // true

要想修复这个问题,简单粗暴的方法是我们直接比对foo的值,如下:

shouldComponentUpdate: function(nextProps, nextState) {
      return this.props.value.foo !== nextProps.value.foo;
}

我们当然可以通过深比对来确定属性或状态是否确实发生了改变,但是这种深比对是非常昂贵的,还记得我们刚出说过shouldComponentUpdate函数的调用非常频繁么?更何况我们为每个model去单独实现一个匹配的深比对逻辑,对于开发人员来说也是非常痛苦的。最重要的是,如果我们不是很小心的处理对象引用关系的话,还会带来灾难。例如下面这个组件:

React.createClass({
      getInitialState: function() {
        return { value: { foo: 'bar' } };
      },

      onClick: function() {
        var value = this.state.value;
        value.foo += 'bar'; // ANTI-PATTERN!
        this.setState({ value: value });
      },

      render: function() {
        return (
              <div>
                <InnerComponent value={this.state.value} />
                <a onClick={this.onClick}>Click me</a>
              </div>
        );
      }
});

起初,InnerComponent组件进行渲染,它得到的value属性为{foo: 'bar'}。当用户点击链接后,父组件的状态将会更新为{ value: { foo: 'barbar' } },触发了InnerComponent组件的重新渲染,因为它得到了一个新的属性:{ foo: 'barbar' }

看上去一切都挺好的,其实问题在于,父组件和子组件供用了同一个对象的引用,当用户触发click事件时,InnerComponent的prop将会发生改变,因此它的shouldComponentUpdate函数将会被调用,而此时如果按照我们目前的shouldComponentUpdate比对逻辑的话,this.props.value.foonextProps.value.foo是相等的,因为事实上,它们同时引用同一个对象哦~所以,我们将会看到,InnerComponent的ui并没有更新。哎~,不信的话,我贴出完整代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>demo</title>
        <!--引入React库-->
        <script src="lib/react.min.js"></script>
        <!--引入JSX转换库-->
        <script src="lib/JSXTransformer.js"></script>
        <!--组件样式-->
    </head>
    <body>
        <!--定义容器-->
        <div id="content"></div>

        <!--声明脚本类型为JSX-->
        <script type="text/jsx">

            var InnerComponent = React.createClass({
                shouldComponentUpdate: function(nextProps, nextState) {
                      return this.props.value.foo !== nextProps.value.foo;
                },
                  render: function() {
                    return (
                          <div>
                            {this.props.value.foo}
                          </div>
                    );
                  }
            });

            var OutComponent = React.createClass({
                  getInitialState: function() {
                    return { value: { foo: 'bar' } };
                  },

                  onClick: function() {
                    var value = this.state.value;
                    value.foo += 'bar'; // ANTI-PATTERN!
                    this.setState({ value: value });
                  },

                  render: function() {
                    return (
                          <div>
                            <InnerComponent value={this.state.value} />
                            <a onClick={this.onClick}>Click me</a>
                          </div>
                    );
                  }
            });

            React.render(<OutComponent />, document.querySelector("#content"));

        </script>
    </body>
</html>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

reactjs性能优化之shouldComponentUpdate 的相关文章

  • 使用Struts2的JSON插件来实现JSON数据传递

    想要实现此功能第一步需要Struts2的核心架包 第二步需要struts2 json plugin 2 3 30架包 在lib文件夹下可以找到 还是借用上次的笔记 来继续写 这个时候我们就不需要用到Servlet了 要使用到Action 配
  • CSS整体界面设计

  • Vue-cli的安装与配置

    Node的下载与安装 工欲善其事必先利其器 在搭建vue的开发环境之前 一定一定要先下载node js vue的运行是要依赖于node的npm的管理工具来实现 1 首先我们进入到node的官网 https nodejs org zh cn
  • CSS之背景样式及边框样式

    1 背景样式 常用属性 background color 背景颜色 background image 背景图片 background repeat 背景图片的平铺方式 background position 背景图片的位置 backgrou
  • JavaScript函数七重关之函数定义

    JavaScript函数七重关的第一关是函数定义 函数定义需要用到function关键字 function myFunction 函数体 document write hello javascrept br 这是函数定义的第一种方法 也可以
  • 前端性能优化

    页面的性能指标 DCL DOMContentLoaded DOM解析完毕 FP First Paint 表示渲染出第一个像素点 FP一般在HTML解析完成或者解析一部分时候触发 FCP First Contentful Paint 表示渲染
  • web前端基础:HTML文字和段落标签

    标题标签 h1 h1 h6 h6 段落标签 p p align对齐属性值 值 描述 left 左对齐 right 右对齐 center 居中对齐 justify 对行进行伸展 每行可以有相等的长度 列表标签 有序列表 ol li 列表项 l
  • webpack打包报错:if (!scriptUrl) throw new Error(“Automatic publicPath is not supported in this browser“)

    翻车现场 ERROR in Error D Work test webpack demo 05 打包图片资源 src index html 104 if scriptUrl throw new Error Automatic publicP
  • 表单页面美化(html、css)

    表单页面美化 html css 效果图片 实现代码 html部分就是平常的输入框 div class mainfont h1 在线预约 h1 div div class form div
  • vue 循环遍历 搜寻资料

    写vue 循环遍历的 大全例子解释 转载连接 https www cnblogs com xulei1992 p 6015416 html https www jqhtml com 49765 html https blog csdn ne
  • CSS动画:Transition与Animation

    本文总结CSS3中两个用来做动画的属性 一个是transition 另一个是animation 差异比较 CSS3 差异 transition 在给定的持续时间内平滑地更改属性值 从一个值到另一个值 也就是只需要指定开始与结束的参数 参数改
  • js __proto__、prototype 、constructor 三者关系总结

    一 proto 属性 proto 怎么读 杠杠 proto 杠杠 proto 读作 dunder proto double underscore proto 的缩写 并且它前后两边 分别是 两个 下划线 由 proto 属性来连接对象 直到
  • VUE element-ui之el-popover弹出框在局部全屏下不显示问题及弹框、小箭头背景修改

    问题 局部全屏后el popover弹出框失效 解决方法
  • html登录页面

    效果图 html代码 index html
  • 微信小程序开发教程

    一 准备 下载微信小程序开发者工具 下载地址 注册微信小程序 前往注册 微信小程序开发文档 前往阅览 打开开发者工具 用微信扫码进入创建页面 填写配置如下 需要注意的是 AppId可以选择已经注册的账号Appid 也可以选择测试号 区别是测
  • laravel路由

    路由 在laravel中 定义路由的地方在routes web php文件中 在使用laravel前必须先定义路由 然后才能在浏览器中访问 routes文件夹中还有一个api php 用于定义api路径 最简单的路由 Route get f
  • JS 时区时间转换

    业务场景 页面服务器时间是东八区时间 页面 JS 功能需要对比服务器时间和用户本地时间 为兼容世界各地时间 需要将用户本地时间转换为东八区时间 基本概念 格林威治时间 格林威治子午线上的地方时 或零时区 中时区 的区时叫做格林威治时间 也叫
  • windows下配置Mysql-5.7.9服务

    第一步 从官方网站下载 mysql 5 7 9 winx64 zip 第二步 解压缩 在根目录下复制my default ini 改名为my ini 第三步 初始化mysql目录 bin mysqld initialize user mys
  • HTML、CSS、JavaScript分别实现什么功能?

    学习Web前端开发基础技术需要掌握 HTML CSS JavaScript 那么这三个都是分别实现什么功能的呢 下面和小编一起来看看吧 一 HTML是网页内容的载体 内容就是网页制作者放在页面上想要让用户浏览的信息 可以包含文字 图片 视频
  • <a>标签的超链接前面会自动加上当前(网站)地址

    当前 网站 地址是 fyh com 在代码里写 a 标签时 会自动在链接前添加 fyh com 例如写如下代码 a href www baidu com baidu a 在浏览器中点击链接会跳转至 fyh com www baidu com

随机推荐

  • 主流的6个Go语言Web框架

    GO 语言爱好者的最佳Web框架 如果你是自己写一个小应用程序 那你可能不需要Web框架 但是如果你要做产品 那么你肯定需要一个好的框架 如果你认为你有相应的知识和经验 你会自己编写所有的这些代码么 你有时间找到一个产品级的外部包来完成工作
  • google各国网址

    google各国网址 巴西 www google com br 瑞士 www google ch 荷兰 www google nl 澳大利亚 www google com au 印度 www google co in 罗马尼亚 www go
  • es每次结果不一样_Elasticsearch 分页坑之---评分一致导致数错乱

    1 背景介绍 最近搞es搜索 match查询默认按照评分排序 发现有一部分数据评分一致 一开始也没注意 客户端调用分页的时候 突然发现数据重复错乱很严重 挖槽顿时觉得 挖槽怎么那么坑 from size 做分页 每次都是重新加载 所以评分一
  • react不能用@引用文件

    方法一 步骤 1 删除node models 步骤 2 重新cnpm install 如果cnpm install时右上角出现eslint 省略号是因为记不清了 点击选择忽略 可能会解决
  • iOS OpenGL渲染YUV数据

    链接 http www jianshu com p 39cde80d60e2 本文主要介绍使用OpenGL ES来渲染I420 YUV420P NV12 YUV420SP 的方法 关于YUV的知识 可以看这里 YUV颜色编码解析 同样会用到
  • [519]matplotlib(一)

    import numpy as np 高斯分布 mean 0 0 cov 0 1 1 0 x y np random multivariate normal mean cov 10000 T 使用NumPy 的 histogram2d 函数
  • 使用RESTful风格api命名接口时,GET方法怎么传递多个参数

    点击上方 码农突围 马上关注 这里是码农充电第一站 回复 666 获取一份专属大礼包 真爱 请设置 星标 或点个 在看 在使用RESTful风格不同于普通借口命名的一点是 它规范使用 来表示资源之间的层级关系 对于普通形式命名的接口 假设需
  • 大型网站架构改进历程:存储的瓶颈

    编者按 本文转自博客园的 夏天的森林 在看这篇之前 大家可以移步看 大型网站架构改进历程 存储的瓶颈 一 二 三 四 上文里我遗留了两个问题 一个问题是数据库做了水平拆分以后 如果我们对主键的设计采取一种均匀分布的策略 那么它对于被水平拆分
  • 运行safari提示:无法启动此程序,因为计算机中丢失 QTCF.dll

    解决办法 1 去百度搜索 QTCF dll 找到一个靠谱的下载地址获取到该dll文件 2 将文件放到 安装目录 Safari Apple Application Support 下边 转载于 https www cnblogs com mi
  • (手工)【sqli-labs40、41】堆叠注入、盲注

    目录 一 推荐 二 手工 SQL注入基本步骤 三 Less40 GET BLIND based String Stacked 3 1 简介 堆叠注入 盲注 字符型注入 3 2 第一步 注入点测试 3 3 第二步 分析过滤 3 4 第三步 判
  • JS 对象引用地址问题处理

    赋值新数组引用地址相同 改变了原数组问题 Object assign This data data item 为dom对象 该传值方式解决for循环中调用ajax始终传入最后一个值的问题 function getYHTypeList BOO
  • 背景建模与前景检测

    From http www cnblogs com xrwang default html page 2
  • 隐藏alert弹框中的localhost:8080(ip地址跟端口号)

    在前端页面中加入下面js代码即可 作用 重写alert方法
  • 路由器配置 校园网账号独立登录 DHCP关闭

    需求 有一个网络有线端口 该网络上网需要认证 比如学校场景中 通过路由器 每个连接路由器信号的用户都需要独立登录自己的网络账号进行验证上网 常见场景 宿舍 教研室 ps 默认设置下 所有连接到该路由器的用户 使用的都是第一个登录的账号 过程
  • 朴素贝叶斯典型的三种算法

    朴素贝叶斯典型的三种算法 朴素贝叶斯主要有三种算法 贝努利朴素贝叶斯 高斯贝叶斯和多项式贝叶斯三种算法 贝努利朴素贝叶斯 也称为二项分布或者0 1分布 元素的结果只有两种可能的结果 高斯贝叶斯 样本符合正态分布或者说高斯分布时采用的算法 多
  • 【华为OD统一考试B卷

    在线OJ 已购买本专栏用户 请私信博主开通账号 在线刷题 运行出现 Runtime Error 0Aborted 请忽略 华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一
  • case when 语法对SQL中的返回字段判断

    CASE WHEN 的使用方法 最近在项目中遇到一个小问题 在这里记录下 数据库表中有一个状态字段status 现在查询的结果要求只返回其中默写特定的状态如 ACTIVE ERROR 其他状态一律返回 OTHER 这就要求我们对查询的结果做
  • 一款非常好用的日期插件(强烈推荐)

    先看看效果 支持年 月 日 时 分 秒 还能快速选择今天 动心了吗 下面是代码 开始日 li class laydate icon style width 200px margin right 10px li
  • Python数据分析numpy学习

    Python数据分析的基本技能包括 1 Python编程语言基础知识 2 数据处理和清洗技能 3 数据可视化技能 4 数据分析和建模技能 Notion AI 文章目录 前言 一 numpy是什么 二 numpy基础 一 数组对象 总结 前言
  • reactjs性能优化之shouldComponentUpdate

    更多内容请关注 性能优化 每当开发者选择将react用在真实项目中时都会先问一个问题 使用react是否会让项目速度更快 更灵活 更容易维护 此外每次状态数据发生改变时都会进行重新渲染界面的处理做法会不会造成性能瓶颈 而在react内部则是