React@16.x(15)PureComponent 和 memo
目录
- 1,什么是 PureComponent
- 2,什么是 memo
- 3,举例
- 3.2,优化1
- 3.1,优化2-函数位置
- 4,注意点
- 4.1,为了提升效率,应该尽量使用 `PureComponent`
- 4.2,不要直接改变之前的状态,而是覆盖
- 4.3,为什么不进行深比较?
1,什么是 PureComponent
纯组件,为了避免不必要的渲染(运行 render)来提升效率。
优化思路:如果一个组件的状态和属性都没有变化,那就不用重新渲染。
具体实现:当某个组件 extends PureComponent 时,则该组件的 shouldComponentUpdate 中会对新旧属性和状态进行浅比较,如果都相等则不会重新渲染(return false)。
// 原理大致如下:
import React, { Component } from "react";export default class Task extends Component {shouldComponentUpdate(nextProps, nextState) {if (isEqual(this.props, nextProps) && isEqual(this.state, nextState)) {return false;}return true;}render() {}
}const isEqual = (obj1, obj2) => {for (const key in obj1) {if (obj1[key] !== obj2[key]) {return false}}return true
}
效果等于:
import React, { PureComponent } from "react";export default class Task extends PureComponent {render() {}
}
2,什么是 memo
React.memo 是一个HOC,相当于给函数组件套了一个 PureComponent,让函数组件也能进行优化。
// 大致原理:
import React, { PureComponent } from "react";function memo(funcComp) {return class Memo extends PureComponent {render() {return <>{funcComp(this.props)}</>}};
}
3,举例
一个展示列表的组件,逻辑很简单,结构如下:
-- TaskContainer-- TaskList-- Task-- TaskAdd(一个输入框,可以新增列表项)
TaskContainer(console.log("container render");)
import React, { Component } from "react";
import TaskAdd from "./TaskAdd";
import TaskList from "./TaskList";export default class TaskContainer extends Component {state = {list: Array.from(new Array(10)).map((item, index) => `任务${index}`),};render() {console.log("container render");return (<div><TaskAddchangeList={(newTask) => {this.setState({list: [...this.state.list, newTask],});}}></TaskAdd><TaskList list={this.state.list}></TaskList></div>);}
}
TaskList(console.log("list render");)
import React, { Component } from "react";
import Task from "./Task";export default class TaskList extends Component {render() {console.log("list render");return (<div>{(this.props.list || []).map((m) => (<Task name={m} key={m}></Task>))}</div>);}
}
TaskAdd(console.log("add render");)
import React, { Component } from "react";export default class TaskAdd extends Component {state = {taskName: "",};render() {console.log("add render");return (<div><inputvalue={this.state.taskName}onChange={(e) => {this.setState({ taskName: e.target.value });}}></input><button onClick={() => this.props.changeList(this.state.taskName)}>添加任务</button></div>);}
}
Task(console.log("task render");)
import React, { Component } from "react";export default class Task extends Component {render() {console.log("task render");return <div>{this.props.name}</div>;}
}
使用:
import React, { Component, Component } from "react";
import TaskContainer from "./components/Pure/TaskContainer";export default class App extends Component {render() {return <TaskContainer></TaskContainer>;}
}
初次渲染时,打印结果:
container render
add render
list render
10次 task render
添加一个列表项时,打印结果:
container render
add render
list render
11次 task render
3.2,优化1
当添加一个列表项时,已经被渲染的10个 task 不应该再次渲染,因为它们自身没有发生任何变化。只应该渲染新增的那一个即可。
此时就可以用到 PureComponent:
export default class Task extends Component {}
// 替换为
export default class Task extends PureComponent {}
这时再添加一个列表项时,在打印结果可以看到 Task 只渲染了一次(新增的)。
container render
add render
list render
task render
3.1,优化2-函数位置
注意到新增列表时,TaskAdd 组件在点击按钮后,不应该再次渲染(不应该打印 add render),因为它的状态和属性也没有发生变化。
也做下替换:
export default class TaskAdd extends Component {}
// 替换为
export default class TaskAdd extends PureComponent {}
发现结果并没有发生变化,add render 依旧会打印。
这时因为在 TaskContainer 中,给组件 TaskAdd 传递的属性 changeList 是直接写在组件上的。这样每次执行 TaskContainer.render() 时,都算作一个新的属性,所以 TaskAdd extends PureComponent 没有生效。
做以下替换即可:
export default class TaskContainer extends Component {render() {return (<div><TaskAddchangeList={(newTask) => {this.setState({list: [...this.state.list, newTask],});}}></TaskAdd></div>);}
}// 替换为
export default class TaskContainer extends Component {changeList = (newTask) => {this.setState({list: [...this.state.list, newTask],});};render() {return (<div><TaskAdd changeList={this.changeList}></TaskAdd></div>);}
}
4,注意点
4.1,为了提升效率,应该尽量使用 PureComponent
4.2,不要直接改变之前的状态,而是覆盖
旧的状态应该是不可变的(immutable),只能通过创建新状态来覆盖之前的状态。
举例,当使用 PureComponent 时,下面的代码不会更新数据。因为进行的是浅比较,此时数组地址相同。
this.state.task.push(xxx)
this.setState({task: this.state.task
})
换成下面的方式就可以了:产生了新数组。
this.setState({task: [...this.state.task, xxx]
})
// 或
this.setState({task: this.state.task.concat(xxx)
})
如果是对象的话:产生了新对象。
this.setState({obj: {...this.state.obj,b: xxx}
})
// 或
this.setState({obj: Object.assign({}, this.state.obj, {b: xxx})
})
4.3,为什么不进行深比较?
因为本来就是为了提升效率,减少 render 执行次数。深比较会比较费时,得不偿失。
以上。
相关文章:
React@16.x(15)PureComponent 和 memo
目录 1,什么是 PureComponent2,什么是 memo3,举例3.2,优化13.1,优化2-函数位置 4,注意点4.1,为了提升效率,应该尽量使用 PureComponent4.2,不要直接改变之前的状态&#…...
[C++11/14新特性] tuple元组介绍
C11 标准新引入了一种类模板,命名为 tuple(中文可直译为元组)。tuple 最大的特点是:实例化的对象可以存储任意数量、任意类型的数据。tuple 的应用场景很广泛,例如当需要存储多个不同类型的元素时,可以使用…...
小熊家务帮day8-day9 客户管理模块2 (用户定位,地址簿,实名认证,银行卡信息上传等功能)
客户管理模块 0.用户定位功能0.1 需求0.2 接口分析0.3 接口开发Controller层开发Service层开发 1.我的地址簿功能1.1 需求1.2 数据库设计1.3 新增地址簿1.3.1 接口设计1.3.2 接口开发Controller层开发Service层开发测试功能 1.4 地址簿查询1.4.1 接口设计1.4.2 接口开发Control…...
amis 事件动作 和 行为按钮 常用用法
行为按钮 action (仅是对click事件的处理) actionType:这是 action 最核心的配置,来指定该 action 的作用类型,支持:ajax、link、url、drawer、dialog、confirm、cancel、prev、next、copy、close。 Butt…...
4K高刷显示器 - 蚂蚁电竞ANT27VU
可以毫不夸张地说,每一局游戏最终能够取得胜利,实际上都与一套极为优秀的电竞 PC 有着紧密的关联,因为其能够提供强大的性能支持与流畅的体验。同样的道理,一套优秀的电竞 PC 若想发挥出最佳的效果,那也都离不开一台能…...
图解支付系统的渠道路由设计
大家好,我是隐墨星辰,今天和大家聊聊渠道路由设计。 这篇文章主要讲清楚:渠道路由是什么,为什么需要渠道路由,渠道路由的几种形态,一个简洁而实用的基于规则的渠道路由设计。 注:有些公司称渠…...
Leecode---347:输出前k个高频元素(使用unordered_map)
题目: 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 思路: 三步: 1、用map来记录每个元素出现的次数 2、按map中的值大小排序,先让其有序,再逆…...
k8s ceph(静态pvc)
1.在 Kubernetes 节点上安装ceph-common 包。这个包包含了连接到 Ceph 集群所需的工具和库。可以使用以下命令在每个节点上安装: sudo apt-get install ceph-common2.在 Kubernetes 中创建一个 Secret 对象,用于存储连接到 Ceph 集群所需的密钥和配置信息…...
Qt QScript 之 C++/JavaScript相互调用
文章目录 Qt Script什么是ECMAScriptQt 中JavaScriptclass 详解Basic UsageQObject对脚本引擎可用使用信号槽connect 三种模式访问属性, 子对象使c++对象可用于用Qt Script编写的脚本C++ 类成员函数可用于脚本C++ 类属性可用于脚本对脚本中的c++对象信号的反应函数对象和本机函…...
可能会引起空指针
PreparedStatement preparedStatement null; preparedStatement conn.prepareStatement(sql); 如果直接下面这个可能会赋值给空指针 因为要在try{}和catch{}里面都用,所以要定义在try外面为null //如果只是测试,可以PreparedStatement preparedStatement conn.prepareSta…...
Linux input输入子系统
Linux input 更多内容可以查看我的github Linux输入子系统框架 Linux输入子系统由驱动层、核心层、事件处理层三部分组成。 驱动层:输入设备的具体驱动程序,负责与具体的硬件设备进行交互,并将底层的硬件输入转化为统一的事件形式ÿ…...
dataworks调度参数
概述 调度参数是DataWorks任务调度时使用的参数,调度参数会根据任务调度的业务时间及调度参数的取值格式自动替换取值,实现在任务调度时间内参数的动态取值。 调度参数通过赋值方式分为自定义参数(推荐)和系统内置变量两大类。 …...
JavaScript第五讲:事件,条件循环语句,错误处理
前言 在编程的世界里,事件、条件和循环语句、以及错误处理是构建任何复杂程序或应用的基石。无论是开发一个简单的网页交互,还是构建一个庞大的企业级系统,这些基础概念都扮演着至关重要的角色。今天星途将通过这篇文章,分别深入…...
BrainGPT1,一个帮你b站点歌放视频的多模态多轮对话模型
BrainGPT1,一个帮你b站点歌放视频的多模态多轮对话模型 返回论文目录 项目地址 模型地址 作者:华东师范大学,计算机科学与技术学院,智能教育研究院的小怪兽会微笑。 介绍 BrainGPT1是一个工具调用多轮对话模型,与G…...
带DSP音效处理D类数字功放TAS5805M中文资料
国产替代D类数字功放中文资料访问下方链接 ACM8628 241W立体声182W单通道数字功放中文寄存器表 内置DSP多种音频处理效果ACM8628M-241W立体声或182W单通道数字功放 1 特性 具有增强处理能力和低功率损耗的 TAS5805M 23W、无电感器、数字输入、立体声、闭环 D 类音频放大器 …...
java中BigDecimal的比较
BigDecimal是Java中的一个类,位于java.math包中,它提供了任意精度的有符号十进制数字的表示,以及对这些数字进行算术运算的方法 BigDecimal的主要用途包括: 1.金融计算:金融领域对数值的精度要求非常高,使…...
张大哥笔记:你卖什么,就反着来卖
普通人打工的一生,就是努力工作,买房,买车,送孩子上好的学校,为了孩子不要输在起跑线上,拼命报各种补习班等,这些都是普通人认为的主流价值观文化,也造就了一批批的赚钱机器…...
Nginx(openresty) 开启gzip压缩功能 提高web网站传输速度
1 开启nginx gzip压缩后,网页的图片,css、js等静态资源的大小会减少,节约带宽,提高传输效率,给用户快的体验,给用户更好的体验. 2 安装 #centos 8.5 yum install gzip 3 配置 #建议统一配置在http段 vim /usr/loca…...
nn.Embedding使用
nn.Embedding使用 Embedding.weight会从标准正态分布中初始化成大小为(num_embeddings, embedding_dim)的矩阵 PE矩阵的作用就是替换这个标准正态分布 input中的标号表示从矩阵对应行获取权重来表示单词 # 1.设置embedding结构 max_seq_len 1000 # 句…...
Qt6 mathgl数学函数绘图
1. 程序环境 Qt6.5.1, mingw11.2mathgl 8.0.1: https://sourceforge.net/projects/mathgl/,推荐下载mathgl-8.0.LGPL-mingw.win64.7z,Windows环境尝试自己编译mathgl会缺失一些库,补充完整也可以自己编译,路径"D:\mathgl-8.0.LGPL-mingw.win64\bin"添加至系统环境…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
