react 原理揭秘
1.目标
A. 能够知道setState()更新数据是异步的
B. 能够知道JSX语法的转化过程
C. 能够说出React组件的更新机制
D. 能够对组件进行性能优化
E. 能够说出虚拟DOM和Diff算法
2.目录
A. setState()的说明
B. JSX语法的转化过程
C. 组件更新机制
D. 组件性能优化
E. 虚拟DOM和Diff算法
3.setState()的说明
3.1 更新数据
A. setState() 是异步更新数据的
B. 注意:使用该语法时,后面的setState()不能依赖于前面的setState()
C. 可以多次调用setState(),只会触发一次重新渲染
1setState.js
import React from "react";class App31 extends React.Component {state = {count: 0,};handleClick = () => {//异步更新操作this.setState({count: this.state.count + 1,});console.log("count1:" + this.state.count);this.setState({count: this.state.count + 1,});console.log("count2:" + this.state.count);};render() {//数据更新了就会调用一次render//但是,如果数据变化一样的,render只调用一次console.log("render");return (<div><h1>计数器:{this.state.count}</h1><button onClick={this.handleClick}>点击</button></div>);}
}export default App31;
index,js
import App31 from "./1setState";
ReactDOM.createRoot(document.getElementById("root")).render(<App31></App31>);
3.2 推荐语法
A. 推荐:使用setState((state,props)=>{})语法
B. 参数state:表示最新的state
C. 参数props:表示最新的props
import React from "react";class App31 extends React.Component {state = {count: 1,};handleClick = () => {// //异步更新操作// this.setState({// count: this.state.count + 1,// });// console.log("count1:" + this.state.count);// this.setState({// count: this.state.count + 1,// });// console.log("count2:" + this.state.count);//推荐语法//注意:这种语法也是异步更新state的,但是会记录最新的state的数据,所以在页面显示为3this.setState((state, props) => {console.log("state1:", state, "props1:", props);return {count: state.count + 1,};});console.log("count:", this.state.count); // 1this.setState((state, props) => {console.log("state2:", state, "props2:", props);return {count: state.count + 1,};});console.log("count:", this.state.count); // 1};render() {//数据更新了就会调用一次render//但是,如果数据变化一样的,render只调用一次console.log("render");return (<div><h1>计数器:{this.state.count}</h1><button onClick={this.handleClick}>点击</button></div>);}
}export default App31;
3.3 第二个参数
A. 场景:在状态更新(页面完成重新渲染)后立即执行某个操作
B. 语法:setState(update [,callback])
import React from "react";class App31 extends React.Component {state = {count: 1,};handleClick = () => {// //异步更新操作// this.setState({// count: this.state.count + 1,// });// console.log("count1:" + this.state.count);// this.setState({// count: this.state.count + 1,// });// console.log("count2:" + this.state.count);//推荐语法//注意:这种语法也是异步更新state的,但是会记录最新的state的数据,所以在页面显示为3// this.setState((state, props) => {// console.log("state1:", state, "props1:", props);// return {// count: state.count + 1,// };// });// console.log("count:", this.state.count); // 1// this.setState((state, props) => {// console.log("state2:", state, "props2:", props);// return {// count: state.count + 1,// };// });// console.log("count:", this.state.count); // 1// 第二个参数,在状态更新(页面完成重新渲染)后立即执行某个操作this.setState((state, props) => {return {count: state.count + 1,};},() => {console.log("状态更新完成,当前count值:", this.state.count);});};render() {//数据更新了就会调用一次render//但是,如果数据变化一样的,render只调用一次console.log("render");return (<div><h1>计数器:{this.state.count}</h1><button onClick={this.handleClick}>点击</button></div>);}
}export default App31;
4.JSX语法的转化过程
A. JSX仅仅是createElement()方法的语法糖(简化语法)
B. JSX语法被@babel/preset-react插件编译为createElement()方法
C. React元素:是一个对象,用来描述你希望的屏幕上看到的内容
const element=<h1 className='greeting'>Hello JSX!</h1>
// const element1=React.createElement('h1',{
// className:'greeting'
// },'Hello JSX!!!')
console.log(element);
// console.log(element1);
ReactDOM.render(element,document.getElementById('root'))
效果图:
5.组件更新机制
A. setState()的两个作用:1.修改state 2.更新组件(UI)
B. 过程:父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树(当前组件及其所有子组件)
首次加载时渲染
点击根组件会触发所有组件,点击左侧父组件1时会触发局部更新,只更新当前组件与子组件,不会触发父组件
2comUpdate.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./comUpdate.css";class App50 extends React.Component {state = {bgColor: "#369",};getBgColor = () => {return Math.floor(Math.random() * 256);};toggleBgColor = () => {this.setState({bgColor: `rgb(${this.getBgColor()},${this.getBgColor()},${this.getBgColor()})`,});};render() {console.log("根组件");return (<divclassName="rootParent"style={{ backgroundColor: this.state.bgColor }}>根组件<button onClick={this.toggleBgColor}>切换根组件颜色</button><div className="app-wrapper"><Parent1></Parent1><Parent2></Parent2></div></div>);}
}
// 左边
class Parent1 extends React.Component {state = {count: 0,};handleClick = (state) => {this.setState((state) => {return {count: state.count + 1,};});};render() {console.log("左侧父组件 1");return (<div className="Parent1"><span>左侧-父组件</span><button onClick={this.handleClick}>点击({this.state.count})</button><div className="parentWrapper1"><Child1></Child1><Child2></Child2></div></div>);}
}
class Child1 extends React.Component {render() {console.log("左侧子组件 1-1");return <div className="child1">左侧子组件1-1</div>;}
}
class Child2 extends React.Component {render() {console.log("左侧子组件 1-2");return <div className="child2">左侧子组件1-2</div>;}
}// 右边
class Parent2 extends React.Component {state = {count: 0,};handleClick = (state) => {this.setState((state) => {return {count: state.count + 2,};});};render() {console.log("右侧父组件 2");return (<div className="Parent1"><span>右侧-父组件</span><button onClick={this.handleClick}>点击({this.state.count})</button><div className="parentWrapper1"><Child3></Child3><Child4></Child4></div></div>);}
}
class Child3 extends React.Component {render() {console.log("右侧子组件 2-1");return <div className="child1">右侧子组件2-1</div>;}
}
class Child4 extends React.Component {render() {console.log("右侧子组件 2-2");return <div className="child2">右侧子组件2-2</div>;}
}
export default App50;
index.js
import App50 from "./2comUpdate";
ReactDOM.createRoot(document.getElementById("root")).render(<App50></App50>);
comUpdate.css
.rootParent {width: 800px;height: 600px;margin: 0 auto;font-weight: 700;font-size: 22px;
}.app-wrapper {display: flex;justify-content: space-between;margin-top: 20px;
}.Parent1 {width: 40%;height: 400px;background-color: pink;
}.parentWrapper1 {display: flex;justify-content: space-between;margin-top: 30px;
}.child1 {width: 40%;height: 300px;background-color: lightgreen;
}.child2 {width: 40%;height: 300px;background-color: lightgrey;
}.Parent2 {width: 40%;height: 400px;background-color: salmon;
}
6.组件性能优化
6.1 减轻state
A. 减轻state:只存储跟组件渲染相关的数据(比如:count/列表数据/loading等)
B. 注意:不用做渲染的数据不要放在state中,比如定时器id等
C. 对于这种需要在多个方法中用到的数据,应该放在this中
6.2 避免不要的重新渲染
A. 组件更新机制:父组件更新会引起子组件也被更新,这种思路很清晰
B. 问题:子组件没有任何变化时也会重新渲染
C. 如何避免不必要的重新渲染吗?
D. 解决方案:使用钩子函数shouldComponentUpdate(nextProps,nextState)
E. 作用:通过返回值决定该组件是否重新渲染,返回true表示重新渲染,false表示不重新渲染
F. 触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate->render)
3performanceOptimize.js
import React from "react";class App62 extends React.Component {state = {count: 0,};handleClick = () => {this.setState((state) => {return {count: state.count + 1,};});};//钩子函数shouldComponentUpdate(nextProps, nextState) {//返回false,阻止组件重新渲染// return false;//最新的状态console.log("最新的state:", nextState);//更新前的状态console.log("this.state:", this.state);return true;}render() {console.log("组件更新了");return (<div><h1>count:{this.state.count}</h1><button onClick={this.handleClick}>点击</button></div>);}
}export default App62;
index.js
import App62 from "./3performanceOptimize";
ReactDOM.createRoot(document.getElementById("root")).render(<App62></App62>);
6.2.1 案例:随机数(nextState)
4App621Random.js
import React from "react";class App621Random extends React.Component {state = {number: 0,};getRandom = () => {this.setState((state) => {return {number: Math.floor(Math.random() * 3),};});};//因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染shouldComponentUpdate(nextProps, nextState) {if (nextState.number === this.state.number) {return false;}return true;}render() {console.log("触发渲染");return (<div><h1>随机数:{this.state.number}</h1><button onClick={this.getRandom}>获取随机数</button></div>);}
}export default App621Random;
index.js
import App621Random from "./4App621Random";
ReactDOM.createRoot(document.getElementById("root")).render(<App621Random></App621Random>
);
6.2.2 案例:随机数(NextProps)
4App621Random.js
import React from "react";class App621Random extends React.Component {state = {number: 0,};getRandom = () => {this.setState((state) => {return {number: Math.floor(Math.random() * 3),};});};//因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染// shouldComponentUpdate(nextProps, nextState) {// if (nextState.number === this.state.number) {// return false;// }// return true;// }render() {console.log("触发父组件的render");return (<div>{/* <h1>随机数:{this.state.number}</h1> */}<ChildNumber number={this.state.number}></ChildNumber><button onClick={this.getRandom}>获取随机数</button></div>);}
}
class ChildNumber extends React.Component {shouldComponentUpdate(nextProps, nextState) {console.log("最新props:", nextProps, ",当前props:", this.props);return nextProps.number !== this.props.number;}render() {console.log("子组件中的render");return <h1>子组件的随机数:{this.props.number}</h1>;}
}export default App621Random;
index.js
import App621Random from "./4App621Random";
ReactDOM.createRoot(document.getElementById("root")).render(<App621Random></App621Random>
);
6.3 纯组件
A. 纯组件:PureComponent与React.Component功能相似
B. 区别:PureComponent内部自动实现了shouldComponentUpdate钩子,无需手动比较
C. 原理:纯组件内部通过对比前后两次props和state的值,来决定是否重新渲染组件
5pure.js
import React from "react";class PureApp extends React.PureComponent {state = {number: 0,};getRandom = () => {this.setState((state) => {return {number: Math.floor(Math.random() * 2),};});};render() {console.log("父组件render");return (<div><h1>父组件随机数:{this.state.number}</h1><ChildPure number={this.state.number}></ChildPure><button onClick={this.getRandom}>获取随机数</button></div>);}
}class ChildPure extends React.PureComponent {render() {console.log("子组件render");return <h1>子组件随机数:{this.props.number}</h1>;}
}
export default PureApp;
index.js
import PureApp from "./5pure";
ReactDOM.createRoot(document.getElementById("root")).render(<PureApp></PureApp>
);
6.3.1 浅层对比
A. 说明:纯组件内部的对比是shallow compare(浅层对比)
B. 对于值类型来说:比较两个值是否相同(直接赋值即可,没有坑)
C.对于引用类型来说:只比较对象的引用(地址)是否相同
D.注意:state或props中属性值为引用类型时,应该创建新数据,不要直接修改数据!
shallow.js
import React from "react";class ShallowRandom extends React.PureComponent {state = {obj: {number: 0,},};getRandom = () => {// 错误演示:直接修改原始对象中属性的值// const newObj = this.state.obj;// newObj.number = Math.floor(Math.random() * 2);// this.setState((state) => {// return {// obj: newObj,// };// });//正确做法:创建新对象const newObj = { ...this.state.obj, number: Math.floor(Math.random() * 2) };this.setState(() => {return {obj: newObj,};});};render() {console.log("父组件render");return (<div><h1>父组件随机数的值:{this.state.obj.number}</h1><ChildRandom number={this.state.obj.number}></ChildRandom><button onClick={this.getRandom}>重新生成随机数</button></div>);}
}class ChildRandom extends React.PureComponent {render() {console.log("子组件render");return <h1>子组件随机数:{this.props.number}</h1>;}
}export default ShallowRandom;
import ShallowRandom from "./6shallow";
ReactDOM.createRoot(document.getElementById("root")).render(<ShallowRandom></ShallowRandom>
);
7.虚拟DOM和DIFF算法
A. React更新视图的思路是:只是state变化就重新渲染视图
B. 特点:思路非常清晰
C. 问题:组件中只有一个DOM元素需要更新时,也得把整个组件的内容重新渲染到页面中?不是
D. 理想状态:部分更新,只更新变化的地方
E. 问题:React是如何做到部分更新的?虚拟DOM配合Diff算法
F. 虚拟DOM:本质上就是一个JS对象,用来描述你希望在屏幕上看到的内容(UI)
7.1 执行过程
A. 初次渲染时,React会根据初始state(Model),创建一个虚拟DOM对象(树)。
B. 根据虚拟DOM生成真正的DOM,渲染到页面中
C. 当数据变化后(setState()),重新根据新的数据,创建新的虚拟DOM对象(树)
A. 与上一次得到的虚拟DOM对象,使用Diff算法对比(找不同),得到需要更新的内容
B. 最终,React只将变化的内容更新(patch)到DOM中,重新渲染到页面
7.2 代码演示
A. 组件render()调用后,根据状态和JSX结构生成虚拟DOM对象
B. 示例中,只更新P元素的文本节点内容
7.vdom.js
import React from "react";class Vdom extends React.PureComponent {state = {number: 0,};getRandom = () => {this.setState(() => {return {number: Math.floor(Math.random() * 3),};});};render() {return (<div><h1>随机数</h1><h1>{this.state.number}</h1><button onClick={this.getRandom}>重新生成</button></div>);}
}export default Vdom;
index.js
import Vdom from "./7vdom";
ReactDOM.createRoot(document.getElementById("root")).render(<Vdom></Vdom>);
8.总结
A. 工作角度:应用第一,原理第二
B. 原理有助于更好的理解React的自身运行机制
C. SetState()异步更新数据
D. 父组件更新导致子组件更新,纯组件提升性能
E. 思路清晰简单为前提,虚拟DOM和Diff保效率
F. 虚拟DOM->state+JSX
G. 虚拟DOM的真正价值从来都不是性能
H. 虚拟DOM使react脱离了浏览器的束缚
相关文章:

react 原理揭秘
1.目标 A. 能够知道setState()更新数据是异步的 B. 能够知道JSX语法的转化过程 C. 能够说出React组件的更新机制 D. 能够对组件进行性能优化 E. 能够说出虚拟DOM和Diff算法 2.目录 A. setState()的说明 B. JSX语法的转化过程 C. 组件更新机制 D. 组件性能优化 E. 虚拟DOM和D…...

el-table实现转置表格
vue版本:vue2.6.10 elementui版本:2.15.14 实现效果:el-table实现行列互换 代码: <template><div class"app-container"><span>原始数据</span><el-table:data"datas"border>…...

(3)(3.1) FlightDeck FrSky发射器应用程序
文章目录 前言 1 概述 2 Turnkey Packages 3 参数说明 前言 Craft and Theory 的 FlightDeck 可让你轻松查看飞行模式、高度、速度、姿态和关键系统警报,包括故障保护和电池错误,如电池不平衡警告和发射机低电量警报。 1 概述 Craft and Theory 的…...

【Unity】导入IAP插件后依赖冲突问题 com.android.billingclient冲突
【Unity】Attribute meta-data#com.google.android.play.billingclient.version 多版本库冲突_unity billingclient-CSDN博客 打开mainTemplate.gradle 找到dependencies { } 在里面末尾加上如下: configurations.all {exclude group: com.android.billingclien…...

docker 转为docker-compose(composerize 命令)
可以使用Composerize将Docker命令转换为Docker Compose文件。 例如:将docker run命令转换为Docker Compose格式,只需用Composerize运行它,如下所示: composerize docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/…...

【Golang切片】
切片 切片的引入内存分析切片的定义切片的遍历切片注意事项 切片的引入 【1】切片(slice)是golang中一种特有的数据类型 【2】数组有特定的用处,但是却有一些呆板(数组长度固定不可变),所以在Go语言的代码…...

React-router的创建和第一个组件
需要先学react框架 首先:找到一个文件夹,在文件夹出打开cmd窗口,输入如下图的口令 npx create-react-app demo 然后等待安装 安装完成 接下来进入创建的demo实例 cd demo 然后可以用如下方式打开vscode code . 注意:不要忽略点号与…...

计算机设计大赛 深度学习猫狗分类 - python opencv cnn
文章目录 0 前言1 课题背景2 使用CNN进行猫狗分类3 数据集处理4 神经网络的编写5 Tensorflow计算图的构建6 模型的训练和测试7 预测效果8 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **基于深度学习猫狗分类 ** 该项目较为新颖&a…...
Linux服务器磁盘及内存用量监控Python脚本(推送钉钉群通知)
文章目录 Python 脚本钉钉推送通知定时任务 Python 脚本 # -*- coding: utf-8 -*- import subprocessdef get_disk_usage():# 执行 df 命令获取磁盘使用情况df_process subprocess.Popen([df, -h, /], stdoutsubprocess.PIPE)output, _ df_process.communicate()output out…...

Android13 Audio框架
一、Android 13音频代码结构 1、framework: android/frameworks/base 1.AudioManager.java :音频管理器,音量调节、音量UI、设置和获取参数等控制流的对外API 2.AudioService.java :音频系统服务(java层),…...

kafka消费者接收不到消息
背景: 对kafka消息进行监听,生产者发了消息,但是消费端没有接到消息,监听代码 消费端,kafka配置 spring.kafka.bootstrap-serverskafka.cestc.dmp:9591 spring.kafka.properties.sasl.jaas.configorg.apache.kafka.…...
Python如何从SQL Server存取数据?
在Python中,你可以使用各种库来连接和操作 SQL Server 数据库。一种常用的库是pyodbc,它是一个用于连接到各种数据库的开源 Python 库,包括 SQL Server。以下是连接到 SQL Server 并存取数据的基本步骤: 1、安装 pyodbc 库&#…...

学校机房Dev c++解决中文乱码问题
工具->编译选项->勾选 编译时加入以下命令 -fexec-charsetGBK -finput-charsetUTF-8 显示中文:工具->编辑器选项->去掉第一个的勾勾。...

基于java+springboot景区行李寄存管理系统设计和实现
基于javaspringboot景区行李寄存管理系统设计和实现 博主介绍:多年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末获取…...

03-grafana的下拉列表选项制作-grafana的变量
一、准备环境 为了实现下拉列表筛选的样例,我们监控两个linux节点; 目前,我们已经有了一个节点了,再添加一个; 二、grafana的仪表盘变量 如果想给仪表盘自定义下拉列表,那么,需要设置变量&#…...

Linux网络编程—— IO多路复用
Linux网络编程—— IO多路复用 1. I/O 多路复用(I/O多路转接)1.1 常见的几种I/O模型 2. select3. poll4. epoll :star: 1. I/O 多路复用(I/O多路转接) I/O 多路复用 使得程序能 同时监听 多个文件描述符,能够提高程序的…...

C++进阶(二) 多态
一、多态的概念 多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会 产生出不同的状态。举个栗子:比如买票这个行为,当普通人买票时,是全价买票;学…...

【C++】set、multiset与map、multimap的使用
目录 一、关联式容器二、键值对三、树形结构的关联式容器3.1 set3.1.1 模板参数列表3.1.2 构造3.1.3 迭代器3.1.4 容量3.1.5 修改操作 3.2 multiset3.3 map3.3.1 模板参数列表3.3.2 构造3.3.3 迭代器3.3.4 容量3.3.5 修改操作3.3.6 operator[] 3.4 multimap 一、关联式容器 谈…...

外包干了6个月,技术退步明显
先说一下自己的情况,本科生,19年通过校招进入广州某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...

华为OD机考- 简单的自动曝光/平均像素
import java.util.Arrays; import java.util.Scanner;public class DemoTest4 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint[] arr Array…...

7种分类数据编码技术详解:从原理到实战
在数据分析和机器学习领域,分类数据(Categorical Data)的处理是一个基础但至关重要的环节。分类数据指的是由有限数量的离散值组成的数据类型,如性别(男/女)、颜色(红/绿/蓝)或产品类…...

NLP学习路线图(三十四): 命名实体识别(NER)
一、命名实体识别(NER)是什么? 命名实体识别(Named Entity Recognition, NER)是自然语言处理中的一项关键序列标注任务。其核心目标是从非结构化的文本中自动识别出特定类别的名词性短语,并将其归类到预定义的类别中。 核心目标:找到文本中提到的命名实体,并分类。 典…...