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脱离了浏览器的束缚

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

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

相关文章

[Vulnhub]靶场 Web Machine(N7)

kali:192.168.56.104 主机探测: arp-scan -l 靶机ip:192.168.56.104 端口扫描 nmap -p- 192.168.56.106 看一下web 目录扫描 gobuster dir -u http://192.168.56.106 -x html,txt,php,bak,zip --wordlist/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt exp…

【QT 5 +Linux下软件qt软件打包+qt生成软件创建可以安装压缩包+学习他人文章+第三篇:学习打包】

【QT 5 Linux下软件qt软件打包qt生成软件创建可以安装压缩包学习他人文章第三篇&#xff1a;学习打包】 1、前言2、实验环境3、自我学习总结-本篇总结&#xff08;1&#xff09;了解安装包的目录结构&#xff08;2&#xff09;了解要编写文件与编写脚本1. control文件2. postin…

NVMFS5113PLWFT1G汽车级功率MOSFET 60V 10A/64A满足AEC-Q101标准

AEC-Q101认证标准详细解读&#xff1a; AEC-Q101是一种汽车电子元件可靠性标准&#xff0c;由汽车电子委员会&#xff08;Automotive Electronics Council&#xff0c;简称AEC&#xff09;制定。该标准旨在确保在汽车环境中使用的电子元件具有足够的可靠性和耐久性。 AEC-Q10…

探索JavaScript中的构造函数,巩固你的JavaScript基础

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

element-ui附件上传及在线查看详细总结,后续赋源码

一、附件上传 1、在element-ui上面复制相应代码 a、accept"image/*,.pdf,.docx,.xlsx,.doc,.xls" 是规定上传文件的类型&#xff0c;若是不限制&#xff0c;可以直接将accept‘all即可&#xff1b; b、:action"action" 这个属性就是你的上传附件的地址&am…

如何解决局域网tcp延迟高来进行安全快速内外网传输呢?

在当今企业运营中&#xff0c;数据的快速流通变得至关重要&#xff0c;但局域网内的TCP延迟问题却成为了数据传输的障碍。本文旨在分析局域网TCP延迟的成因&#xff0c;并探讨几种企业数据传输的常见模式&#xff0c;以及如何为企业选择合适的传输策略&#xff0c;以确保数据在…

java之servlet

动态的web资源开发技术 不同的用户&#xff0c;或者携带不同的参数&#xff0c;访问服务器 服务器添加判断层&#xff0c;实现访问不同的web资源

【iOS ARKit】协作 Session 实例

协作 Session 使用注意事项 协作 Session 是在 ARWorldMap 基础上发展起来的技术&#xff0c;ARWorldMap 包含了一系列的地标、ARAnchor 及在观察这些地标和 ARAnchor 时摄像机的视场&#xff08;View&#xff09;。如果用户在某一个位置新创建了一个 ARAnchor&#xff0c;这时…

禅道安装与使用

文章目录 1.下载2.安装 1.下载 进入禅道官网下载 2.安装 登录后

uniapp生成app包引导用户开启通知权限和热更新

uniapp生成app包引导用户开启通知权限和热更新 引导用户开启通知权限 export function setPermissions() {// #ifdef APP-PLUS if (plus.os.name Android) {var main plus.android.runtimeMainActivity();var pkName main.getPackageName();var uid main.getApplicationI…

【免费】两阶段鲁棒优化matlab实现——CCG和benders

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 程序采用matlab复现经典论文《Solving two-stage robust optimization problems using a column-and-constraint generation method》算例&#xff0c;实现了C&CG和benders算法两部分内容&#xff0c;通过…

1.3 vue ui框架-element-ui框架

1 前言 ElementUI是一套基于VUE2.0的桌面端组件库&#xff0c;ElementUI提供了丰富的组件帮助开发人员快速构建功能强大、风格统一的页面。 ElementUI官网 https://element.eleme.io 2 安装 运行命令 cnpm i element-ui -S -S表示只在该项目下安装&#xff0c;不是全局安…

基于YOLOv8深度学习的复杂场景下船舶目标检测系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

GL绘制自定义线条4_使用OpenGL ES实现钢笔效果

在以前的文章里http://t.csdnimg.cn/TgCtl&#xff0c;我简述了如何使用OpenGL ES实现光滑的粗线条的绘制效果&#xff0c;在闲暇时间我把它再进一步进化&#xff0c;实现了端点长度按照压感大小实现伸缩的逻辑&#xff0c;从而实现了如下的笔锋效果&#xff1a; 书写过程中的效…

包管理工具之npm也慌了?

起因 因为npm的种种问题,我很早就换成了pnpm和yarn(但是其实npm也在使用),已经很久没有关注npm的功能更新了。最近无意间进入Node18版本的安装目录,发现其除了常规的node,npm等默认安装了一个新的包corepack,这个就是今天我要分享的东西了。 注: 我因为18版本的node上…

【STM32】STM32学习笔记-FLASH闪存(48)

00. 目录 文章目录 00. 目录01. FLASH简介02. 闪存模块组织03. FLASH基本结构04. FLASH解锁05. 使用指针访问存储器06. 程序存储器编程07. 选项字节08. 选项字节编程09. 选项字节擦除10. 器件电子签名11. 附录 01. FLASH简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选…

灰度负载均衡和普通负载均衡有什么区别

灰度负载均衡&#xff08;Gray Load Balancing&#xff09;与普通负载均衡的主要区别在于它们服务发布和流量管理的方式。 灰度负载均衡 目的&#xff1a;主要用于灰度发布&#xff0c;即逐步向用户发布新版本的服务&#xff0c;以减少新版本可能带来的风险。工作方式&#x…

【软考】UML中的图之通信图

目录 1. 说明2. 图示3. 特性4. 例题4.1 例题1 1. 说明 1.通信图强调收发消息的对象的结构组织2.早期版本叫做协作图3.通信图强调参加交互的对象和组织4.首先将参加交互的对象作为图的顶点&#xff0c;然后把连接这些对象的链表示为图的弧&#xff0c;最后用对象发送和接收的消…

Google发布Genie硬杠Sora:通过大量无监督视频训练最终生成可交互虚拟世界

前言 Sora 问世才不到两个星期&#xff0c;谷歌的世界模型也来了&#xff0c;能力看似更强大(嗯&#xff0c;看似)&#xff1a;它生成的虚拟世界自主可控 第一部分 首个基础世界模型Genie 1.1 Genie是什么 Genie是第一个以无监督方式从未标记的互联网视频中训练的生成式交互…

vue-electron 项目创建记录及注意事项

vue-electron 项目创建记录及注意事项 1、使用vue ui或者命令行创建vue项目 2、添加electron插件 3、安装element-plus: npm install --save element-plus 4、修改配置文件如下图: vue.config.js增加配置&#xff1a; pluginOptions:{ electronOutput: { contextIsolation…