当前位置: 首页 > news >正文

16.React学习笔记.React更新机制

一. 发生更新的时机以及顺序##

image.png
  1. props/state改变
  2. render函数重新执行
  3. 产生新的VDOM树
  4. 新旧DOM树进行diff
  5. 计算出差异进行更新
  6. 更新到真实的DOM

二. React更新流程##

React将最好的O(n^3)的tree比较算法优化为O(n)。

  • 同层节点之间相互比较,不跨节点。
  • 不同类型的节点,产生不同的树结构:如果该节点不同,会将旧tree中该节点的子树全部删掉。直接生成新的子树,挂载到DOM中。
  • 开发中,可以通过key来指定哪些节点在不同的渲染下保持稳定。

三. 不同情况##

情况一:对比不同类型的元素
当节点为不同的元素,React会拆卸原有的树,并且建立起新的树。

  • 当一个元素改变,会触发一个完整的重建流程。
  • 卸载一棵树时,对应的DOM节点也会被销毁,组件实例将执行componentWillUnmount()方法。
  • 建立一棵新树时,对应得DOM节点会被创建以及插入到DOM中,组件实例将执行componentWillMount()方法,紧接着componentDidMount()方法。
  • 子树销毁,元素不会复用。####

情况二:对比同一类型的元素

  • 当对比两个相同类型的React元素时,React会保留DOM节点,仅比对及更新有改变的属性, 比如下面例子:
image.png
  • React知道只需要修改DOM元素上的className属性。

image.png

-当 更新style属性时,React仅更新有所改变的属性,没有变化的属性不会变。

  • 如果是同类型的组件元素:组件会保持不变,React会更新该组件的props,并且调用componentWillReceiveProps()和componentWillUpdate()方法;
  • 下一步,调用render()方法,diff算法将在之前的结果以及新的结果中进行递归。

情况三:对子节点进行递归

image.png
  • 默认条件下,当递归DOM节点的子元素时,React会同时遍历两个子元素的列表;当产生差异时,生成一个mutation。
  • 如上图,前两个比较相同,不会有mutation。
  • 最后一个比较,产生一个mutation,将其插入到新的DOM树中即可。
  • 当然这是理想情况
image.png

如果我们在中间插入一条数据:

  • React会对每一个子元素产生一个mutation,而不是保持其不变。
  • 这种方式会有一定的性能问题。

所以这时需要key来优化###

四. key优化##

  1. 在尾部添加数据
  • 有无key意义并不大。
  1. 在前面插入数据
  • 这种情况,在没有key的情况下,所有li都需要进行修改。
  • 当子元素拥有key时,React使用key来匹配原有树上的子元素以及最新树上的子元素:这种情况下:原有的元素只是发生了位移。
  render() {return (<div><h2>电影列表</h2><ul>{this.state.movies.map((item,index) => {return <li key={item}>{item}</li>})}</ul><button onClick={e => this.insertMovie()}>添加电影</button></div>)}
  1. key的注意事项:
  • key应该是唯一的。
  • key不要使用随机数(随机数在下一次render时,会重新生成一个数字)。
  • 使用index作为key,对性能是没有优化的,id比较合适。

五. 组件嵌套的render调用##

import React, { Component } from 'react'// Header
function Header() {console.log("Header被调用");return <h2>我是Header组件</h2>
}// Banner
class Banner extends Component {render() {console.log('Banner的render函数被调用');return <h3>我是bannner组件</h3>}
}function ProductList() {console.log("ProductList被调用");return (<ul><li>商品列表1</li><li>商品列表2</li><li>商品列表3</li><li>商品列表4</li><li>商品列表5</li></ul>)
}
// Main
class Main extends Component {render() {console.log('Main render函数被调用');return (<div><Banner /><ProductList /></div >)}
}
// Footer
function Footer() {console.log("Footer被调用");return <h2>我是Footer组件</h2>
}export default class App extends Component {constructor(props) {super(props);this.state = {counter: 0,}}render() {console.log('App render函数被调用');return (<div><h2>当前计数:{this.state.counter}</h2><button onClick={e=>this.increment()}>+1</button><Header /><Main /><Footer /></div>)}increment(){this.setState({counter: this.state.counter + 1})}
}
  • 调用一个无关的函数,界面改变时,按理来说不应该让别的没有改变的东西重新render。
  • 这个例子中我们在前面插入了一个<h2>和<button>标签,现在点击按钮时全局都会重新渲染。
  • 现在对其进行优化

六. 组件嵌套的render调用的优化##

  • 调用完setState后,不想render时阻断其渲染。
  • 使用shouldComponentUpdate() {}这个生命函数,默认情况下其返回true,也就是重新渲染;手动设置为false后,将不会重新渲染,但不影响初始化的渲染。
  • 我们的目的是:想要阻断时阻断(事件发生后与界面没有依赖),不想阻断时渲染,如下代码。
  shouldComponentUpdate(nextProps, nextState){if(this.state.counter !== nextState.counter){return true;}return false;}

以上为简单情况,当组件变多后,情况将很复杂,函数/类组件都需要考虑到###

  • 每个类都设置该生命周期函数太麻烦。

  • 我们通过继承PureComponent而不是Component来进行简化,其会对state和props进行比较来决定是否重新render。

  • shouldComponentUpdate在源码中进行更新时,决定是否需要render。

  • 回溯到源码ReactFiberClassComponent中时,有如下方法:

function checkShouldComponentUpdate(workInProgress,ctor,oldProps,newProps,oldState,newState,nextContext,
) {const instance = workInProgress.stateNode;
// 判断有无该生命周期函数if (typeof instance.shouldComponentUpdate === 'function') {    
startPhaseTimer(workInProgress, 'shouldComponentUpdate');
// -----------------
// 核心代码const shouldUpdate = instance.shouldComponentUpdate(newProps,newState,nextContext,);stopPhaseTimer();return shouldUpdate;}
// -----------------if (ctor.prototype && ctor.prototype.isPureReactComponent) {return (!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState));}return true;
}
  • 该方法最终返回true/false。
  • 这个对应React中PureComponent的特点
    image.png
  • 其中isPureReactComponent属性对应上面放出来源代码中对原型中isPureReactComponent属性的判断。
  • 如果有isPureReactComponent属性,则对oldProps,oldState和newProps,newState进行一个浅层比较。
  • 通过浅层比较来判断是否发生了改变。

追溯到shallowEqual方法源码中

import is from './objectIs';const hasOwnProperty = Object.prototype.hasOwnProperty;/*** Performs equality by iterating through keys on an object and returning false* when any key has values which are not strictly equal between the arguments.* Returns true when the values of all keys are strictly equal.*/
function shallowEqual(objA: mixed, objB: mixed): boolean {if (is(objA, objB)) {return true;}if (typeof objA !== 'object' ||objA === null ||typeof objB !== 'object' ||objB === null) {return false;}const keysA = Object.keys(objA);const keysB = Object.keys(objB);if (keysA.length !== keysB.length) {return false;}// Test for A's keys different from B.for (let i = 0; i < keysA.length; i++) {if (!hasOwnProperty.call(objB, keysA[i]) ||!is(objA[keysA[i]], objB[keysA[i]])) {return false;}}return true;
}export default shallowEqual;
  • 先判断两个对象,相同则true,返回后取反,表示不需要更新。
  • 接着分别判断两个对象,不是对象或者为null时返回false,强制刷新
  • 然后将两个对象中的keys取出来,若长度不想等则返回false,若相等,则对其中属性进行比较,不相等则返回false进行刷新。

这就回到我们案例中,只有App,Header,Footer的render被调用。

  • PureComponent对props和state进行shallowEqual 。
  • Main,Banner,ProductList没有依赖任何props/state,所以没有重新渲染。
  • 开发中只需要shallowEqual深层比较非常浪费性能。
  • PureComponent可以解决类组件的render调用,但解决不了函数式组件

七. memo的使用,优化函数式组件##

memo为高阶组件。

const MemoHeader = memo(function Header() {console.log("Header被调用");return <h2>我是Header组件</h2>
})
image.png
  • 我们将原来的函数组件传入memo函数中,生成一个新的组件类型。
  • 将Footer也进行转换,这样只有App重新渲染了,
  • 但我们没有更改ProductList,其也没有重新渲染,原因是在Main中,重新渲染已经被阻止了
  • 为了以防万一,也可以用memo优化。

理论上:建议所有类组件都用PureComponent,所有函数组件都包裹memo



喜欢的朋友记得点赞、收藏、关注哦!!!

相关文章:

16.React学习笔记.React更新机制

一. 发生更新的时机以及顺序## image.png props/state改变render函数重新执行产生新的VDOM树新旧DOM树进行diff计算出差异进行更新更新到真实的DOM 二. React更新流程## React将最好的O(n^3)的tree比较算法优化为O(n)。 同层节点之间相互比较&#xff0c;不跨节点。不同类型的节…...

【Elasticsearch】词干提取(Stemming)

词干提取是将一个词还原为其词根形式的过程。这确保了在搜索过程中&#xff0c;一个词的不同变体能够匹配到彼此。 例如&#xff0c;walking&#xff08;行走&#xff09;和walked&#xff08;走过&#xff09;可以被还原到同一个词根walk&#xff08;走&#xff09;。一旦被还…...

【AI论文】10亿参数大语言模型能超越405亿参数大语言模型吗?重新思考测试时计算最优缩放

摘要&#xff1a;测试时缩放&#xff08;Test-Time Scaling&#xff0c;TTS&#xff09;是一种通过在推理阶段使用额外计算来提高大语言模型&#xff08;LLMs&#xff09;性能的重要方法。然而&#xff0c;目前的研究并未系统地分析策略模型、过程奖励模型&#xff08;Process …...

【设计模式】【行为型模式】状态模式(State)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f4eb; 欢迎V&#xff1a; flzjcsg2&#xff0c;我们共同讨论Java深渊的奥秘 &#x1f…...

PostgreSQL错误: 编码“UTF8“的字符0x0xe9 0x94 0x99在编码“WIN1252“没有相对应值

错误介绍 今天遇到一个错误&#xff0c;记录一下 2025-02-10 17:04:35.264 HKT [28816] 错误: 编码"WIN1252"的字符0x0x81在编码"UTF8"没有相对应值 2025-02-10 17:04:35.264 HKT [28816] 错误: 编码"UTF8"的字符0x0xe9 0x94 0x99在编码&quo…...

Mac ARM 架构的命令行(终端)中,删除整行的快捷键是:Ctrl + U

在 Mac ARM 架构的命令行&#xff08;终端&#xff09;中&#xff0c;删除整行的快捷键是&#xff1a; Ctrl U这个快捷键会删除光标所在位置到行首之间的所有内容。如果你想删除光标后面的所有内容&#xff0c;可以使用&#xff1a; Ctrl K这两个快捷键可以帮助你快速清除当…...

Vue2下判断有新消息来时以站内信方式在页面右下角弹出

以下是完整的Vue2全局通知组件实现方案&#xff0c;包含自动挂载和全局调用方法&#xff1a; 第一步&#xff1a;创建通知组件 <!-- src/components/Notification/index.vue --> <template><div class"notification-container"><transition-g…...

AI语言模型的技术之争:DeepSeek与ChatGPT的架构与训练揭秘

云边有个稻草人-CSDN博客 目录 第一章&#xff1a;DeepSeek与ChatGPT的基础概述 1.1 DeepSeek简介 1.2 ChatGPT简介 第二章&#xff1a;模型架构对比 2.1 Transformer架构&#xff1a;核心相似性 2.2 模型规模与参数 第三章&#xff1a;训练方法与技术 3.1 预训练与微调…...

网络安全中的account和audit区别

一、AWD介绍 AWD&#xff1a;Attack With Defence&#xff0c;即攻防对抗&#xff0c;比赛中每个队伍维护多台服务器&#xff08;一般两三台&#xff0c;视小组参赛人数而定&#xff09;&#xff0c;服务器中存在多个漏洞&#xff08;web层、系统层、中间件层等&#xff09;&a…...

Visual Studio 使用 “Ctrl + /”键设置注释和取消注释

问题&#xff1a;在默认的Visual Studio中&#xff0c;选择单行代码后&#xff0c;按下Ctrl /键会将代码注释掉&#xff0c;但再次按下Ctrl /键时&#xff0c;会进行双重注释&#xff0c;这不是我们想要的。 实现效果&#xff1a;当按下Ctrl /键会将代码注释掉&#xff0c;…...

【密评】 | 商用密码应用安全性评估从业人员考核题库(23)

在GM/T0048《智能密码钥匙密码检测规范》中,产品的对称算法性能应满足哪个标准中的要求()。 A.GM/T 0016《智能密码钥匙密码应用接口规范》 B.GM/T 0017《智能密码钥匙密码应用接口数据格式规范》 C.GM/T 0027《智能密码钥匙技术规范》 D.GM/T 0028《密码模块安全技术要求》…...

【MySQL】幻读 案例分析

目录 假设1&#xff1a;只在 id5 这一行加锁&#xff0c;其他行不加锁&#xff1f; 幻读的定义 幻读的场景 假设1 产生的问题&#xff1a;语义被破坏 假设1 产生的问题&#xff1a;数据一致性 结论&#xff1a; 假设1不成立 假设2&#xff1a;扫描过程中每一行都加上写锁…...

10bit VS 8bit 视频:色彩深度的较量,谁才是视觉盛宴的王者?

10bit 和 8bit 视频 10bit 视频和 8bit 视频的主要区别在于色彩深度和细节表现能力。10bit 视频具有更高的色彩深度和更丰富的细节表现,能够提供更平滑的色彩过渡和更真实的图像质量,但需要更多的存储空间和带宽。8bit 视频则在存储和传输方面更加高效,适合于对存储空间和带…...

讲解下MySql的外连接查询在SpringBoot中的使用情况

在Spring Boot中使用MySQL的外连接查询时&#xff0c;通常通过JPA、MyBatis或JDBC等持久层框架来实现。外连接查询主要用于从多个表中获取数据&#xff0c;即使某些表中没有匹配的记录。外连接分为左外连接&#xff08;LEFT JOIN&#xff09;、右外连接&#xff08;RIGHT JOIN&…...

蓝桥杯试题:归并排序

一、问题描述 在一个神秘的岛屿上&#xff0c;有一支探险队发现了一批宝藏&#xff0c;这批宝藏是以整数数组的形式存在的。每个宝藏上都标有一个数字&#xff0c;代表了其珍贵程度。然而&#xff0c;由于某种神奇的力量&#xff0c;这批宝藏的顺序被打乱了&#xff0c;探险队…...

物联网(IoT)如何与人工智能(AI)的结合

物联网&#xff08;IoT&#xff09;与人工智能&#xff08;AI&#xff09;的结合是当前技术发展的重要趋势&#xff0c;通常被称为 AIoT&#xff08;人工智能物联网&#xff09;。这种结合通过将AI的计算能力和数据分析能力与物联网的海量设备连接能力相结合&#xff0c;实现了…...

一致性Hash算法延伸至Redis分片扩容使Lua脚本失效如何解决

文章部分内容来源&#xff1a;小林coding 问题场景&#xff1a;我们需要用Lua脚本&#xff0c;并且这个Lua脚本需要用到两个Key&#xff0c;但这两个Key必须命中同一台机器才可以&#xff0c;不然Lua脚本就会执行失败。如果集群扩容可能会导致两个Key落到不同的节点上导致Lua脚…...

Idea 插件 Quickly-Code-Toolkit

使用说明 &#xff08;一&#xff09;全局设置 Paging Wrapper Setting&#xff08;分页设置&#xff09; 功能&#xff1a;主要用于在方法写入时&#xff0c;为返回参数提供分页包装类。设置方式&#xff1a;需准确填写分页包装类的全限定名&#xff0c;例如&#xff1a;com…...

先进制造aps专题二十九 基于ai智能体的生产排程和工厂生产仿真引擎的设计

上文中&#xff0c;我们说&#xff0c;通常的做法是&#xff0c;可以先通过排产仿真引擎产生生产计划&#xff0c;再在工厂仿真引擎里仿真执行&#xff0c;这样可以预先分析计划和执行的差异情况并进行调整优化 这里的产生生产计划&#xff0c;仿真生产执行和数据分析都是人工…...

【Cocos TypeScript 零基础 15.1】

目录 见缝插针UI脚本针脚本球脚本心得_旋转心得_更改父节点心得_缓动动画成品展示图 见缝插针 本人只是看了老师的大纲,中途不明白不会的时候再去看的视频 所以代码可能与老师代码有出入 SIKI_学院_点击跳转 UI脚本 import { _decorator, Camera, color, Component, directo…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...