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.hqwc.cn/news/500013.html

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

相关文章

VL53L8CX驱动开发(1)----驱动TOF进行区域检测

VL53L8CX驱动开发----1.驱动TOF进行区域检测 概述视频教学样品申请源码下载主要特点硬件准备技术规格系统框图应用示意图区域映射生成STM32CUBEMX选择MCU 串口配置IIC配置LPn 设置X-CUBE-TOF1串口重定向代码配置Tera Term配置演示结果 概述 VL53L8CX是一款8x8多区域ToF测距传感…

centos物理电脑安装过程(2024年1月)

开机时&#xff1a;CtrlAltDelete键重启电脑 重启开始时&#xff1a;按F11&#xff0c;桌面弹出蓝色框&#xff0c;选择第二个SSK SFD142 1.00&#xff0c;回车 选择install centos7安装 选择后弹出选择安装选项&#xff0c;选择语言 连接无线网络 安装设置&#xff0c;选择磁…

数字生活的未来:Web3如何改变我们的日常

随着技术的飞速发展&#xff0c;我们的生活正变得日益数字化。而Web3作为一种新型的互联网模式&#xff0c;正以前所未有的方式改变着我们的日常生活。在本文中&#xff0c;我们将深入探讨Web3技术的特点以及它如何改变我们的数字生活。 1. Web3的特点 Web3是基于区块链技术和…

基于Siamese网络的zero-shot意图分类

原文地址&#xff1a;Zero-Shot Intent Classification with Siamese Networks 通过零样本意图分类有效定位域外意图 2021 年 9 月 24 日 意图识别是面向目标对话系统的一项重要任务。意图识别(有时也称为意图检测)是使用标签对每个用户话语进行分类的任务&#xff0c;该标签…

指针的传递使用场景

C语言函数调用时为值传递&#xff0c;实参赋值给形参&#xff0c;形参值改变不会影响实参&#xff08;原理&#xff1a;两个参数地址不同&#xff09;&#xff0c;若要函数改变实参值&#xff0c;应当传递实参的地址&#xff0c;参考以下实例。 代码展示&#xff1a; #includ…

MySQL-MHA搭建、故障测试

一、架构说明 MHA&#xff08;Master High Availability&#xff09;是一个用于 MySQL 主从复制管理和自动故障转移的开源工具集。MHA 的主要目的是提供 MySQL 环境的高可用性和自动故障转移功能&#xff0c;确保在主库发生故障时能够快速切换到备库&#xff0c;降低业务中断时…

在Windows中安装PyTorch

文章目录 1. 创建虚拟环境2. 检查显卡版本和CUDA3. 下载链接4. 下载5. 等待6. 检测 1. 创建虚拟环境 具体查看我之前写的 《在Windows中利用Python的venv和virtualenv创建虚拟环境》 2. 检查显卡版本和CUDA 这种情况是需要电脑上有单独的英伟达的显卡、或者英伟达的显卡和集显…

「MySQL」增删查改

在操作数据库中的表时&#xff0c;需要先使用该数据库&#xff1a; use database;新增 创建表 先用 use 指定一个数据库,然后使用 create 新增一个表 比如建立一个学生表 mysql> use goods; mysql> create table student(-> name varchar(4),-> age int,-> …

2024-02-29(Flink)

1.Flink原理&#xff08;角色分工&#xff09; 2.Flink执行流程 on yarn版&#xff1a; 3.相关概念 1&#xff09;DataFlow&#xff1a;Flink程序在执行的时候会被映射成一个数据流模型&#xff1b; 2&#xff09;Operator&#xff1a;数据流模型中的每一个操作被称作Operat…

从0到1使用C++实现一个模拟器-1-【实现最简CPU】

文章目录 uint64_tstdstd::arrayCPU和CU类构造函数size_tstatic_caststd::ifstreamriscv64-unknown-elf-objcopy -O binary add-addi add-addi.binriscv64-unknown-elf-gcc -Wl,-Ttext0x0 -nostdlib -o add-addi add-addi.s-wlstd::hex std::setw() std::setfill()各自的用法he…

【Leetcode每日一刷】动态规划算法: 62. 不同路径、63. 不同路径 II

博主简介&#xff1a;努力学习和进步中的的22级计科生博主主页&#xff1a; Yaoyao2024每日一句: “ 路虽远&#xff0c;行则将至。事虽难&#xff0c;做则可成。” 前言 前言&#xff1a;动规五部曲 以下是《代码随想录》作者总结的动规五部曲 确定dp数组&#xff08;dp tab…

iSlide插件2024免费版(包含52 个PPT设计辅助功能,9 大在线资源库,以及超 50 万 专业)

一、功能介绍 iSlide是一款专为PowerPoint设计的插件&#xff0c;它集合了众多设计与效率提升的功能&#xff0c;帮助用户更快速、更美观地制作演示文稿。 主题设计&#xff1a;提供多种设计主题&#xff0c;用户只需一键应用&#xff0c;即可为幻灯片赋予统一的视觉风格。智…