08react基础-react原理

devtools/2025/3/10 11:41:22/
setState()更新数据
  • setState()更新数据是异步的
  • 注意:使用该语法,后面的setState不要依赖前面setState的值
  • 多次调用setState,只会触发一次render
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {state = {count: 1}handleClick = () => {// 此处,更新state// 注意:异步更新数据的!!!this.setState({count: this.state.count + 1})console.log('count:', this.state.count) // 1this.setState({count: this.state.count + 1 // 1 + 1})console.log('count:', this.state.count) // 1}render() {console.log('render')return (<div><h1>计数器:{this.state.count}</h1><button onClick={this.handleClick}>+1</button></div>)}
}
ReactDOM.render(<App />, document.getElementById('root'))
执行结果.png
setState()推荐语法
  • 基础语法:
this.setState((state, props) => {
return {
count: state.count + 1}
})
console.log(this.state.count) // 1
  • 完整代码:
import React from 'react'
import ReactDOM from 'react-dom'/* setState() 推荐语法
*/class App extends React.Component {state = {count: 1}handleClick = () => {// 推荐语法:// 注意:这种语法也是异步更新state的!this.setState((state, props) => {return {count: state.count + 1 // 1 + 1}})this.setState((state, props) => {console.log('第二次调用:', state)return {count: state.count + 1}})console.log('count:', this.state.count) // 1}render() {return (<div><h1>计数器:{this.state.count}</h1><button onClick={this.handleClick}>+1</button></div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
执行结果.png
setState()第二个参数
  • 场景:在状态更新(页面完成重新渲染)后立即执行某个操作
  • 语法:setState(update[,callback])
this.setState(
(state, props) => {},
() => {console.log('这个回调函数会在状态更新后立即执行')}
)

例子:

this.setState(
(state, props) => {},
() => {
document.title = '更新state后的标题:' + this.state.count}
)

具体代码:

import React from 'react'
import ReactDOM from 'react-dom'/* setState() callback
*/class App extends React.Component {state = {count: 1}handleClick = () => {this.setState((state, props) => {return {count: state.count + 1}},// 状态更新后并且重新渲染后,立即执行:() => {console.log('状态更新完成:', this.state.count) // 2console.log(document.getElementById('title').innerText)document.title = '更新后的count为:' + this.state.count})console.log(this.state.count) // 1}render() {return (<div><h1 id="title">计数器:{this.state.count}</h1><button onClick={this.handleClick}>+1</button></div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
JSX语法的转化过程
image.png
  • JSX仅仅是createElement() 方法的语法糖(简化语法)
  • JSX语法被 @babel/preset-react 插件编译为createElement() 方法
  • React 元素: 是一个对象,用来描述你希望在屏幕上看到的内容


    image.png
组件更新机制
  • setState() 的两个作用

    • 修改state
    • 更新组件
  • 过程:父组件重新渲染时,也会重新渲染子组件,但只会渲染当前组件子树(当前组件以其所有子组件)


    image.png
import React from 'react'
import ReactDOM from 'react-dom'/* 组件更新机制
*/import './index.css'// 根组件
class App extends React.Component {state = {color: '#369'}getColor() {return Math.floor(Math.random() * 256)}changeBG = () => {this.setState(() => {return {color: `rgb(${this.getColor()}, ${this.getColor()}, ${this.getColor()})`}})}render() {console.log('根组件')return (<div className="app" style={{ backgroundColor: this.state.color }}><button onClick={this.changeBG}>根组件 - 切换颜色状态</button><div className="app-wrapper"><Parent1 /><Parent2 /></div></div>)}
}// ------------------------左侧---------------------------class Parent1 extends React.Component {state = {count: 0}handleClick = () => {this.setState(state => ({ count: state.count + 1 }))}render() {console.log('左侧父组件')return (<div className="parent"><h2>左侧 - 父组件1<button onClick={this.handleClick}>点我({this.state.count})</button></h2><div className="parent-wrapper"><Child1 /><Child2 /></div></div>)}
}class Child1 extends React.Component {render() {console.log('左侧子组件 - 1')return <div className="child">子组件1-1</div>}
}
class Child2 extends React.Component {render() {console.log('左侧子组件 - 2')return <div className="child">子组件1-2</div>}
}// ------------------------右侧---------------------------class Parent2 extends React.Component {state = {count: 0}handleClick = () => {this.setState(state => ({ count: state.count + 1 }))}render() {console.log('右侧父组件')return (<div className="parent"><h2>右侧 - 父组件2<button onClick={this.handleClick}>点我({this.state.count})</button></h2><div className="parent-wrapper"><Child3 /><Child4 /></div></div>)}
}class Child3 extends React.Component {render() {console.log('右侧子组件 - 1')return <div className="child">子组件2-1</div>}
}
class Child4 extends React.Component {render() {console.log('右侧子组件 - 2')return <div className="child">子组件2-2 </div>}
}ReactDOM.render(<App />, document.getElementById('root'))
更新子组件时.png

更新根组件时..png
组件性能优化
  • 1.减轻state
  • 减轻state:只存储跟组件渲染相关的数据(比如:count/ 列表数据 /loading等)
  • 注意:不用做渲染的数据不要放在state中
  • 对于这种需要在多个方法中用到的数据,应该放到this中
class Hello extends Component {
componentDidMount() {
// timerId存储到this中,而不是state中
this.timerId = setInterval(() => {}, 2000)}
componentWillUnmount() {
clearInterval(this.timerId)}
render() { … }
}
  • 2.避免不必要的重新渲染
  • 组件更新机制:父组件更新会引起子组件也被更新,这种思路很清晰
  • 问题:子组件没有任何变化时也会重新渲染
  • 如果避免不必要的重新渲染?
  • 解决方式:使用钩子函数shouldComponentUpdate(nextProps, nextState)
    • 在这个函数中,nextPropsnextState是最新的状态以及属性
  • 作用:这个函数有返回值,如果返回true,代表需要重新渲染,如果返回false,代表不需要重新渲染
  • 触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate => render)
class Hello extends Component {
shouldComponentUpdate() {
// 根据条件,决定是否重新渲染组件
return false
}
render() {…}
}

随机数案例 如果随机获取的数值和之上一次一样不更新组件否则就更新

import React from 'react'
import ReactDOM from 'react-dom'/* 组件性能优化:
*/// 生成随机数
class App extends React.Component {state = {number: 0}handleClick = () => {this.setState(() => {return {number: 1}})}// 因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染shouldComponentUpdate(nextProps, nextState) {console.log('最新状态:', nextState, ', 当前状态:', this.state)return nextState.number !== this.state.number// if (nextState.number !== this.state.number) {//   return true// }// return false// if (nextState.number === this.state.number) {//   return false// }// return true}render() {console.log('render')return (<div><h1>随机数:{this.state.number}</h1><button onClick={this.handleClick}>重新生成</button></div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
  • nextprops:
import React from 'react'
import ReactDOM from 'react-dom'/* 组件性能优化:
*/// 生成随机数
class App extends React.Component {state = {number: 0}handleClick = () => {this.setState(() => {return {number: Math.floor(Math.random() * 3)}})}// 因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染// shouldComponentUpdate(nextProps, nextState) {//   console.log('最新状态:', nextState, ', 当前状态:', this.state)//   return nextState.number !== this.state.number// }render() {// console.log('render')return (<div><NumberBox number={this.state.number} /><button onClick={this.handleClick}>重新生成</button></div>)}
}class NumberBox extends React.Component {shouldComponentUpdate(nextProps) {console.log('最新props:', nextProps, ', 当前props:', this.props)// 如果前后两次的number值相同,就返回false,不更新组件return nextProps.number !== this.props.number// if (nextProps.number === this.props.number) {//   return false// }// return true}render() {console.log('子组件中的render')return <h1>随机数:{this.props.number}</h1>}
}ReactDOM.render(<App />, document.getElementById('root'))
纯组件
作用以及使用
  • 纯组件: PureComponentReact.Component 功能相似
  • 区别: PureComponent 内部自动实现了 shouldComponentUpdate钩子,不需要手动比较
  • 原理:纯组件内部通过分别比对前后两次 props和state的值,来决定是否重新渲染组件
class Hello extends React.PureComponent {
render() {
return (
<div>纯组件</div>)}
}
  • 实现原理
  • 说明:纯组件内部的对比是 shallow compare(浅层对比)
  • 对于值类型来说:比较两个值是否相同
const obj = { number: 0 }
const newObj = obj
newObj.number = 2
console.log(newObj === obj) // true
  • 引用类型:只比对对象的引用地址是否相同
state = { obj: { number: 0 } }
// 错误做法
state.obj.number = 2
setState({ obj: state.obj })
// PureComponent内部比较:
最新的state.obj === 上一次的state.obj // true,不重新渲染组件
  • 注意:state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据
// 正确!创建新数据
const newObj = {...state.obj, number: 2}
setState({ obj: newObj })
// 正确!创建新数据
// 不要用数组的push / unshift 等直接修改当前数组的的方法
// 而应该用 concat 或 slice 等这些返回新数组的方法
this.setState({
list: [...this.state.list, {新数据}]
})

案例:

import React from 'react'
import ReactDOM from 'react-dom'/* 组件性能优化:
*/// 引用类型:
const obj = { number: 0 }
const newObj = obj
newObj.number = 2
console.log(newObj === obj) // true// 生成随机数
class App extends React.PureComponent {state = {obj: {number: 0}}handleClick = () => {// 正确做法:创建新对象const newObj = { ...this.state.obj, number: Math.floor(Math.random() * 3) }this.setState(() => {return {obj: newObj}})// 错误演示:直接修改原始对象中属性的值/* const newObj = this.state.objnewObj.number = Math.floor(Math.random() * 3)this.setState(() => {return {obj: newObj}}) */}render() {console.log('父组件重新render')return (<div><h1>随机数:{this.state.obj.number}</h1><button onClick={this.handleClick}>重新生成</button></div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
虚拟 DOM 和 Diff 算法
  • React更新视图的思想是:只要state变化就重新渲染视图
  • 特点:思路非常清晰
  • 问题:组件中只有一个DOM元素需要更新时,也得把整个组件的内容重新渲染吗? 不是这样的
  • 理想状态:部分更新,只更新变化的地方
  • React运用的核心点就是 虚拟DOM 配合 Diff 算法
虚拟DOM

虚拟 DOM:本质上就是一个 JS 对象,用来描述你希望在屏幕上看到的内容(UI)。


image.png
Diff算法

执行过程

  • 初次渲染时,React会根据初始化的state(model),创建一个虚拟DOM对象(树)
  • 根据虚拟DOM生成真正的DOM,渲染到页面
  • 当数据变化后(setState()),会重新根据新的数据,创建新的虚拟DOM对象(树)
  • 与上一次得到的虚拟DOM对象,使用Diff算法比对(找不同),得到需要更新的内容
  • 最终,React只将变化的内容更新(patch)到DOM中,重新渲染到页面
image.png

代码演示:

import React from 'react'
import ReactDOM from 'react-dom'/* 虚拟DOM 和 Diff算法
*/// 生成随机数
class App extends React.PureComponent {state = {number: 0}handleClick = () => {this.setState(() => {return {number: Math.floor(Math.random() * 2)}})}// render方法调用并不意味着浏览器中的重新渲染!!!// render方法调用仅仅说明要进行diffrender() {const el = (<div><h1>随机数:</h1><p>{this.state.number}</p><button onClick={this.handleClick}>重新生成</button></div>)console.log(el)return el}
}ReactDOM.render(<App />, document.getElementById('root'))
image.png
最后编辑于:2025-02-24 21:39:42


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


http://www.ppmy.cn/devtools/165989.html

相关文章

交大智邦后端Java笔试题

交大智邦后端Java笔试题 简答题 只要一个类加上了Component注解&#xff0c;就一定能成为一个Spring Bean吗&#xff1f;如果不是&#xff0c;请举出反例。 不一定 扫描范围不包括 com.code.lab.web.component 或者被 ComponentScan 显式排除 通过 excludeFilters 手动排除特定…

Android Glide 配置与初始化模块源码深度剖析

一、引言 在 Android 开发中&#xff0c;图片加载是一个常见且重要的功能。Glide 作为一款强大的图片加载库&#xff0c;因其高效、灵活和易于使用的特点&#xff0c;被广泛应用于各种 Android 应用中。Glide 的配置与初始化模块是整个库的基础&#xff0c;它允许开发者根据不…

为什么要开源?

互联网各领域资料分享专区(不定期更新): Sheet 正文 开源(Open Source)是软件、硬件或知识产品将其源代码或设计公开,允许任何人自由使用、修改和分发的模式。开源的核心不仅是“免费”,更是一种协作和透明的理念。以下是开源的主要动因和优势: 一、技术驱动:提升质量…

【python爬虫】酷狗音乐爬取

本次爬取的音乐仅有1分钟试听&#xff0c;完整音乐需要下载客户端 一、 初步分析 登陆酷狗音乐后随机选取一首歌&#xff0c;在请求里发现一段mp3文件&#xff0c;复制网址&#xff0c;确实是我们需要的url。 复制音频的名字&#xff0c;搜索找到发起请求的网址&#xff0c;发…

计算机视觉|3D卷积网络VoxelNet:点云检测的革新力量

一、引言 在科技快速发展的背景下&#xff0c;3D 目标检测技术在自动驾驶和机器人领域中具有重要作用。 在自动驾驶领域&#xff0c;车辆需实时、准确感知周围环境中的目标物体&#xff0c;如行人、车辆、交通标志和障碍物等。只有精确检测这些目标的位置、姿态和类别&#x…

PreTrainedModel 类代码分析:_load_pretrained_model

1 _load_pretrained_model 调用流程&#xff1a; 1. 初始化标志和变量&#xff1a; 判断权重文件是否是 safetensors 格式&#xff08;is_safetensors&#xff09;。判断是否使用了量化器&#xff08;is_quantized&#xff09;。初始化状态字典的文件夹和索引&#xff08;sta…

【Git】创建,切换分支

理解分支 这里开始介绍Git的杀手级功能之一&#xff1a;分支。 分支就是科幻电影里的平行宇宙&#xff0c;当你正在电脑前努力学习C的时候&#xff0c;另一个你正在另一个平行宇宙里努力学习JAVA。 如果两个平行宇宙互不干扰&#xff0c;那对现在的你也没啥影响。不过&#…

单体架构、集群、分布式、微服务的区别!

目录 一、 单体架构 &#x1f9f1;二、 集群 &#x1f46f;‍♀️&#x1f46f;‍♂️三、 分布式架构 &#x1f3e2;四、 微服务架构 &#x1f3d8;️五、总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;可以多多…