react18 核心知识点杂记1

  • 类组件 如何渲染为真实dom

入口:
const root = ReactDOM.createRoot(document.getElementById('root'))root.render(类组件)⬇️ 类组件内部render() {return (<div>12</div>)}⬇️ (经过babel-preset-react-app 把jsx语法,编译为h函数形式)
React.createElement('div', null, '12')⬇️ ( React.createElement返回值是一个普通 JavaScript 对象,即 React 元素)该元素是对UI结构以及属性的描述。{ 	 type: 'div',props: { children: '123' },key: null,ref: null,_owner: null,_store: {}}⬇️  然后render 将 react元素 生成为虚拟DOM 再由react内部将虚拟dom构建为树,进行diff计算等,再创建真实dom⬇️ componentDidMount生命周期钩子
组件挂载完成

代码定义 → JSX转换 → 创建元素 → Fiber树构建 → 协调(Diff) → DOM更新 → 生命周期回调


  • 类组件的生命周期

在这里插入图片描述


  • vote组件 属性初始化

class vote extends React.Component{render() {return (<div>....</div>
}
}
  • 使用vote组件
import voteconst root = ReactDOM.createRoot(document.getElementById('root'))root.render(<Vote {title = 'xxx'}/>)

在这里插入图片描述

  • 类SetState 机制

在这里插入图片描述

  • ❗️‼️注意: react18中,setState 本身并不是传统意义上的“异步操作”(如 Promise 或 setTimeout),而是 ​批量更新(Batching)​机制 的表现。React 内部维护一个更新队列,在 ​同一渲染周期内 的多个 setState 会被合并处理,多个状态更新合并(批量处理)后再统一计算新状态和触发渲染,这可能导致状态更新看起来“延迟生效”
    所以本文中讲的异步操作都理解为 ​批量更新(Batching)​机制
    在这里插入图片描述
  • 🆘 react16 的setState 在合成事件 又有区别
    在这里插入图片描述

  • react 合成事件

  handleVue() {console.log(this)}handleVue2 = (x, y) => {console.log(x, y, this)}render() {console.log('渲染完成')return (<div className="vote"><button onClick={this.handleVue}>vue</button> {/* this指向undefined */}<button onClick={() => this.handleVue()}>vue</button> {/* this指向组件实例 */}<button onClick={this.handleVue.bind(this)}>vue</button> {/* this指向组件实例 */}<button onClick={this.handleVue2.bind(1, 2)}>vue</button> {/* this指向组件实例,且预传参 */}</div>)}

在这里插入图片描述

  • 合成事件的事件委托机制
    在这里插入图片描述
    合成事件都会委托给入口文件的挂载结点 #root上,合成事件与原生事件能在同一节点并行,到那时合成事件的绑定要早于原生事件,因为合成事件是react内部绑定的。

‼️ 注意:

事件传播分离:合成事件与原生事件的传播路径独立。阻止合成事件冒泡不会影响原生事件,反之亦然。

在这里插入图片描述


  • purecomponent 是基于堆内存地址的浅比较,一个对象的某个深层属性更改,它判定为地址不变,不会触发页面更新 ❌,这是错误的。所以它不适合深层次的属性的 state 或 props 的组件。

💯Hooks组件

💝 this: 因为类组件需要 react内部new 来实例化,所以要处理this绑定的问题,而函数组件是 纯函数,每次渲染都会重新执行该函数组件,产生一个私有上下文,所以不涉及this的处理。

❤️‍🔥 想了解hooks组件的更新机制,先理解一下Fiber

Fiber 是 React 维护的一种数据结构,包含组件相关的所有信息。

每个React (函数 ,类) 组件都对应 一个Fiber 节点,所有的组件组成了 完整的Fiber树。

  • React 维护两棵 Fiber 树:​Current Tree​(当前屏幕显示)和 ​WorkInProgress Tree​(正在构建的更新)。
  • 状态变更时,两棵树变更的节点 进行对比,复用大部分属性​(如 type、key),仅更新变化的 props 或 state。
fiber = {type: ComponentFunction,   组件类型(函数组件)memoizedState: hook1,      **指向 Hook 链表的头部**stateNode: {},             组件实例(类组件)或 DOM 节点// 其他调度相关属性(如优先级、副作用链表等)
}

❤️‍🔥 再了解一下Hook

Hook(钩子),eg:useState为一个hook函数,它的每一次调用,都会生成一个React内部维护的hook对象

const [count, setCount] = useState(0)hook对象 = {memoizedState: 0,   当前页面展现的值baseState: 0,       所有更新(queue)处理后的新基准.当所有已处理的更新(queue 中的更新)被消费后,baseState 会被更新为最新的 memoizedState**但是** 渲染被中断(如高优先级任务插队),React 会回退到 baseState 重新计算状态。queue: null,           更新队列(存储 setCount 触发的更新动作)next: null,            指向下一个 Hook 的指针
};

hook对象的queue队列作用如下:

const [count, setCount] = useState(0);
const handleClick = () => {setCount(1);       setCount(2);  
};// handleClick()后的hook对象
hook = {memoizedState: 0,  // 当前状态值baseState: 0,      // 基础状态(初始值)queue: { setCount(1),setCount(2);  }, // 更新队列next: null, // 指针
}

每次调用hook的副作用函数后,就会加入到对应hook对象的更新队列中,等待react 内部更新。


每一个hook函数自上而下产生的hook对象 ,都会以链表的形式维护在 fibermemoizedState

function Component() {const [name, setName] = useState("Alice");  		   hook对象1const [age, setAge] = useState(30);                  hook对象2const [location, setLocation] = useState("Nowhere"); hook对象3
}

首次渲染(Mount)链表指向

Fiber.memoizedState → Hook对象1 → Hook对象2 → Hook对象3

后续渲染(Update)时,复用hook对象

Fiber.memoizedState → Hook对象1 → Hook对象2 → Hook对象3

React 再次执行组件函数,​按相同顺序调用 useState
但不会重新创建 Hook 对象,而是按顺序遍历 Fiber链表


💘 setState对象形式 通过「闭包」链接hook对象

为什么要闭包?
闭包的作用是确保同一组件实例在不同渲染周期中的状态和上下文被正确隔离
是实例状态持久化的唯一手段。
每一个useState 执行产生的hook对象,hook中变量对外暴露,外部引用了该变量, 为一个闭包的产生。

 const [count, setCount] = useState(0)  来自React内部存储的count,而不是组件函数的局部变量。const handleClick = () => {setCount(count + 1)  引用了count,闭包产生}

useState 简要更新逻辑

let hook = [memoizedState: any,    // 当前状态值(对于 useState,保存的是 state)baseState: any,        // 基础状态(用于计算更新)baseQueue: Update<any> | null,  // 待处理的更新队列queue: UpdateQueue<any> | null, // 状态更新队列(保存 setState 触发的更新)next: Hook | null,];function useState(newValue) {// 定义 setState 函数const setState = (newValue) => {// 收集更新阶段:将更新加入当前 Hook 的队列// 加入队列hooks.queue.push(newValue);
------------------------------------------上下为两个不同的阶段// 处理更新阶段for (const update of hooks.queue) {currentState = typeof update === 'function' ? update.action(currentState)  // 函数式更新,传入当前状态: newState = update.action;    // 对象式更新,直接替换值}hooks.memoizedState = currentState; // 更新最终状态hooks.queue = [];    // 触发重新渲染(模拟 React 的更新机制)scheduleRender();};// 返回值 和 副作用函数 setStatereturn [hook.memoizedState, setState];
}// 模拟重新渲染
function scheduleRender() {currentHookIndex = 0; // 重置索引,准备下一次渲染render();
}

✅:Fiber有了,hook对象有了,闭包有了,可以来梳理 hook 是如何让函数组件动态起来了。

1. 组件首次渲染(Mount):- Fiber 创建 → 初始化 Hook 链表(useState) → setState 闭包绑定 Fiber 和 Hook。2. 用户触发 setState:- setState 通过闭包找到对应的 Hook → 将更新加入 Hook.queue。→ 调度更新(scheduleUpdateOnFiber)。3. React 调度更新:- 进入渲染阶段 → 从 Fiber 读取 Hook 链表 → 按顺序处理每个 Hook 的队列。→ 计算新状态 → 更新 Hook.memoizedState。4. 组件函数重新执行-渲染(Update):- 使用最新的状态生成新 VDOM → 协调(Reconciliation) → 提交(Commit)到 DOM。

‼️ useState 返回的变量, 是函数组件外, React内部状态在该次渲染中的状态值快照

当组件执行,并引用了这个变量时,闭包就会形成,
所以调用栈的同步代码,或是将要进入任务队列的回调,都会提前绑定好该闭包的引用
当任务队列的事件推到调用栈时,直接拿到前面已经绑定的闭包值来使用。

useState 在每次组件渲染时都会被调用,但不会重新初始化状态(除非组件重新挂载)。它的作用是返回当前状态值,而不是重新执行初始化逻辑


‼️ 注意
执行到 setstate() 这一行时,会把它注册到 hook.queue 对象中,且不会立马执行 (该hook由react维护,不进入堆栈,任务队列)
接着执行后续代码,同步的直接执行,异步的进入队列
当调用栈清空
此时hook.queue 会先于任务队列执行,
当hook.queue中的更新动作全部消费完,就会触发组件重新调用渲染,
然后再执行刚刚加入任务队列的事件。

💝所以,可以理解为一轮事件周期内,queue中的setstate() 的消费,要快于任务队列。

❤️‍🔥 看代码理解

const [count, setCount] = useState(0)
const handleClick = () => {setCount(count + 1)setTimeout(() => {console.log(count, 'count')})}
  1. handleclick 入调用栈
  2. setcount调用,进入hook.queue 等待批量更新,引用了count ,在堆中产生count的闭包
  3. settimeout 的回调绑定count的闭包引用(定义时绑定),再加入任务队列
  4. 消费hook.quene ,触发Fiber调度,更新count,渲染页面
  5. 拉取任务队列的定时器回调,并执行。

🧪 setState 函数式更新

与对象式不同的唯一一点就是变量的获取

  • 对象式直接是绑定闭包引用来进行计算
  • 而函数式, 回调函数在链表中执行时,永远上一个的执行后的pedding状态,是下一个回调任务的参数。称为链式状态传递。

⏰ 简单的 setState 更新模型。
在这里插入图片描述


flushsync

flushSync会绕过 React 的自动批处理,强制同步处理状态更新,但仅在新旧状态值实际变化时触发组件渲染


⚠️ useState 自带性能优化

  1. ​短路优化:当新状态与旧状态相同(Object.is 比较)时,React 会跳过渲染。

  2. 更新队列同一批中,多个函数式的,会基于前一个pending的状态更改(不论是否函数式), 将最新值穿给下一个,最后触发一次更新渲染

     例如,假设当前count是0:调用setCount(0 +1) → 计划更新到1接着调用setCount(prev => prev +10),这里的prev是前一个pending计划的状态,即1,所以结果为11。
    
  3. 更新队列同一批中,多个对象形式的,后面的覆盖前面的,仅采取最后一次action触发一次更新渲染

export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(count + 5)}
}

1:点击按钮,render执行几次?count最终为?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(count + index)}return (<div><p>Count: {count}</p><button onClick={handleClick}>按钮</button></div>)
}

2:点击按钮,render执行几次?count最终为?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(prev => prev + 10)}return (<div><p>Count: {count}</p><button onClick={handleClick}>按钮</button></div>)
}

3:点击按钮,render执行几次?count最终为?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(prev => prev + index)}return (<div><p>Count: {count}</p><button onClick={handleClick}>按钮</button></div>)
}

4:点击按钮,render执行几次?count最终为?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {flushSync(() => {setCount(count + 1)})}return (<div><p>Count: {count}</p><button onClick={handleClick}>按钮</button></div>)
}

5:点击按钮,render执行几次?count最终为?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {flushSync(() => {setCount(prev => prev + index)})}return (<div><p>Count: {count}</p><button onClick={handleClick}>按钮</button></div>)
}

6:点击按钮,render执行几次?count最终为?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {setCount(count + 1)setCount(prev => prev + 10)}return (<div><p>Count: {count}</p><button onClick={handleClick}>按钮</button></div>)
}

6:点击按钮,render执行几次?count最终为?


export default function Counter() {console.log('render')const [count, setCount] = useState(0)const handleClick = () => {for (let index = 0; index < 10; index++) {flushSync(() => {setCount(count + index)})}return (<div><p>Count: {count}</p><button onClick={handleClick}>按钮</button></div>)
}

7:点击按钮,render执行几次?count最终为?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/72544.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matlab 汽车传动系统的振动特性分析

1、内容简介 Matlab 186-汽车传动系统的振动特性分析 可以交流、咨询、答疑 2、内容说明 略 摘要&#xff1a;汽车动力传动系统是一个具有多自由度的、连续的、有阻尼系统。传动系统的振动主要有横向振动、扭转振动、纵向振动。并且汽车传动系统的扭转振动是一个非常重要的振…

JDBC技术基础

文章目录 1. JDBC概述1.1 数据的持久化1.2 Java中的数据存储技术1.3 JDBC介绍1.4 JDBC体系结构1.5 JDBC程序编写步骤 2. 获取数据库连接2.1 引入JAR包2.2 要素一&#xff1a;Driver接口实现类2.2.1 Driver接口介绍2.2.2 加载与注册JDBC驱动 2.3 要素二&#xff1a;URL2.4 要素三…

Matlab自学笔记四十八:各类型缺失值的创建、判断、替换、移位和处理方法

1.各类数据缺失值的创建 程序示例如下&#xff1a; a[nan 1 2 3] %数值型缺失值 s[string(missing) "a" "b"] %字符串型缺失值 t[NaT datetime(2018,8,8)] %时间型缺失值 isnan(a) %判断数值型缺失值 运行结果&#xff1a; a NaN 1 2 …

如何使用IDEA Maven构建本地jar包和POM文件?

在开发Java项目时&#xff0c;很多人会借助Maven来管理项目依赖与构建。用IntelliJ IDEA&#xff08;简称IDEA&#xff09;来构建本地jar包和POM文件&#xff0c;是一个常见的需求。下面我就给你详细讲解一下这个过程&#xff0c;确保你也能轻松上手&#xff01; 准备工作 首…

QT入门笔记2

目录 一、前言 二、串口助手实现 2.1、串口 2.1.1、可用串口信息-QSerialPortInfo 2.1.2、打开串口-QSerialPort 2.1.3、串口发送接收信息 2.2、定时器-QTimer 2.3、常用属性类型转换&#xff08;会更新&#xff09; 2.4、子控件组规则命名优化 一、前言 这个是学习Q…

Word 小黑第40套

对应大猫43 主题 -浏览主题 -选择W样式标准文件就行 1级段落和2级段落&#xff08;用项目符号不影响原本段落文字符号 颜色修改为自动&#xff09; 整段变红的 不是把光标定位到红色字体那里 要选择几个红色字体 再创建样式 插入的空白页一定要是下一页&#xff0c;不能插空白…

基于yolo11+flask打造一个精美登录界面和检测系统

这个是使用flask实现好看登录界面和友好的检测界面实现yolov11推理和展示&#xff0c;代码仅仅有2个html文件和一个python文件&#xff0c;真正做到了用最简洁的代码实现复杂功能。 测试通过环境&#xff1a; windows x64 anaconda3python3.8 ultralytics8.3.81 flask1.1.…

SQLMesh系列教程:利用date_spine宏构建日期序列实践指南

引言&#xff1a;为什么需要日期维度表&#xff1f; 在数据分析和报表开发中&#xff0c;日期维度表是不可或缺的基础结构&#xff0c;其中包括一定日期范围的日期序列&#xff0c;每个序列包括对应日期属性&#xff0c;如年季月日、是否周末等。无论是计算日粒度销售额、分析…

【蓝桥杯】省赛:神奇闹钟

思路 python做这题很简单&#xff0c;灵活用datetime库即可 code import os import sys# 请在此输入您的代码 import datetimestart datetime.datetime(1970,1,1,0,0,0) for _ in range(int(input())):ls input().split()end datetime.datetime.strptime(ls[0]ls[1],&quo…

2024浙江大学计算机考研上机真题

2024浙江大学计算机考研上机真题 2024浙江大学计算机考研复试上机真题 2024浙江大学计算机考研机试真题 2024浙江大学计算机考研复试机试真题 历年浙江大学计算机复试上机真题 历年浙江大学计算机复试机试真题 2024浙江大学计算机复试上机真题 2024浙江大学计算机复试机试真题 …

Typora 使用教程(标题,段落,字体,列表,区块,代码,脚注,插入图片,表格,目录)

标题 一个#是一级标题, 2个#是二级标题, 以此类推, 最多可达六级标题 示例 输入#号和标题后回车即可 注意: #和标题内容之间需要存在空格(一个或多个均可), 没有空格就会变成普通文字 标题快捷键 Ctrl数字 1-6 可以快速调成对应级别的标题 (选中文本/把光标放在标题上再按…

`FisherTrainer` 的自定义 `Trainer` 类:累积梯度的平方并求平均来近似计算 Fisher 信息矩阵

FisherTrainer 的自定义 Trainer 类:累积梯度的平方并求平均来近似计算 Fisher 信息矩阵 用于计算模型参数的 Fisher 信息矩阵的近似值 整体目标 Fisher 信息矩阵用于衡量模型参数的不确定性,其在优化问题中可以帮助我们更准确地更新模型参数,避免陷入局部最优。在代码中,…

网页制作代码html制作一个网页模板

制作一个简单而实用的网页模板&#xff1a;HTML基础入门 在数字时代&#xff0c;网页已成为信息展示和交流的重要平台。HTML&#xff08;HyperText Markup Language&#xff09;作为网页制作的基础语言&#xff0c;为开发者提供了构建网页的基本框架。本文将带你了解如何使用H…

二阶近似 是什么意思

二阶近似 是什么意思 一、二阶近似的概念与举例 二阶近似是数学分析中通过泰勒展开对函数进行近似的方法,保留到二阶项(即包含一阶导数和二阶导数)。在优化问题(如模型训练)中,常用于近似损失函数,帮助更精准地更新模型参数。 举例: 假设损失函数为 L ( θ ) \mathc…

ImGui 学习笔记(四)—— 实现每窗口背景色

ImGui 的窗口背景仅通过全局的 style 控制&#xff0c;这一点不方便于我们设置特定窗口的背景透明度&#xff08;一般不用于调整颜色&#xff09;&#xff0c;分析代码&#xff0c;我们可以找到 ImGui::RenderWindowDecorations 函数&#xff1a; void ImGui::RenderWindowDec…

Python虚拟环境完全指南:用venv管理项目依赖,避免环境冲突的N个技巧

引言&#xff1a;当你的第3个Python项目开始报错时… “明明在Demo项目能跑的代码&#xff0c;移植到新项目就报错&#xff1f;” 你可能正经历着Python开发者的成年礼——依赖冲突。本文手把手教你用Python内置的venv模块打造隔离的虚拟环境&#xff0c;从此告别pip install引…

【后端开发面试题】每日 3 题(十三)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12903849.html &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享后端开发面试中常见的面试题给大家&#xff0c;每天的题目都是独…

C#入门学习记录(三)C#中的隐式和显示转换

C#类型转换&#xff1a;隐式与显式转换的机制与应用 在C#的强类型体系中&#xff0c;数据类型转换是实现数据交互和算法逻辑的基础操作。当数值类型范围存在包含关系&#xff0c;或对象类型存在继承层次时&#xff0c;系统通过预定义的转换规则实现类型兼容处理。隐式转换&…

Linux FILE文件操作2- fopen、fclose、fgetc、fputc、fgets、fputs验证

目录 1.fopen 打开文件 1.1 只读打开文件&#xff0c;并且文件不存在 1.2 只写打开文件&#xff0c;并且文件不存在 1.3 只写打开文件&#xff0c;并且文件存在&#xff0c;且有内容 1.4 追加只写打开文件&#xff0c;并且文件不存在 2. fclose 关闭文件 3. fgetc 读取一…

如何检查CMS建站系统的插件是否安全?

检查好CMS建站系统的插件安全是确保网站安全的重要环节&#xff0c;对于常见的安全检查&#xff0c;大家可以利用以下几种有效的方法和工具&#xff0c;来帮你评估插件的安全性。 1. 检查插件来源和开发者信誉 选择可信来源&#xff1a;仅从官方插件库或可信的第三方开发者处…