当前位置: 首页 > news >正文

一文讲清楚React的render优化,包括shouldComponentUpdate、PureComponent和memo

文章目录

  • 一文讲清楚React的render优化,包括shouldComponentUpdate、PureComponent和memo
  • 1. React的渲染render机制
  • 2. shouldComponentUpdate
    • 2.1 先上单组件渲染,验证state变化
    • 2.2 上父子组件,验证props
  • 2. PureComponent
    • 2.1 单组件验证state
    • 2.2 父子组件验证props
  • 3.React.memo
  • 4. 总结

一文讲清楚React的render优化,包括shouldComponentUpdate、PureComponent和memo

1. React的渲染render机制

  • 我们都知道,在React中,state和props的改变以及父组件人render执行都会造成render的重新执行
  • 关于这点不懂的,看这篇文章一文讲清楚React中的render机制
  • 但是在某些复杂的业务场景下,如果大量的子组件在没有发生任何变化的情况下被更新,就会造成一定的性能影响
  • 我们希望的是,只有在子组件state和props有变化,且判定为需要重新渲染的情况下再执行render进行渲染
  • 能不能做到呢,恭喜,还真能做到
  • 我们可以通过三驾马车,shouldComponentUpdate、PureComponent、memo来实现

2. shouldComponentUpdate

  • 见名知意,组件应不应该更新,所以这里应该有一个条件判断,如果返回true,则更新,如果返回false,则不更新
  • 判断什么呢,判断state和props是否发生变化,如果变化了,说明页面需要重新渲染,那我们就是返回true,反之亦然
  • 话不多说,上代码

2.1 先上单组件渲染,验证state变化

import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={num:0}}handleClick=()=>{this.setState({num:0})}render(){console.log('render() has been executed')return(<div><div>{this.state.num}</div><button onClick={this.handleClick}>button</button></div>)}
}
export default App
  • 我们首次运行后发现,render() has been executed被打印了一次,说明render执行了一次,这是正常的,首席渲染
  • 然后我们点击button,发现render() has been executed又被打印了一次,说明render又执行了一次渲染,但是我们在button里面没有改变state的状态,num还是0,render理论上不执行才是最好的选择
  • 这时候shouldComponentUpdate就派上用场了
  • 我们只要在shouldComponentUpdate里面判断state是否发生了变化,如果没有,返回false,这样render就不会执行了
  • 上代码
import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={num:0}}shouldComponentUpdate(nextProps,nextState,nextContent){//nextProps表示即将接受的新的props//nextState表示即将接受的心得State,//这里我们通过判断nextState.num === this.state.num,如果是返回false,阻止render执行;如果true,render继续渲染if(nextState.num ===  this.state.num){return false }else{return true}}handleClick=()=>{this.setState({num:0})}render(){console.log('render() has been executed')return(<div><div>{this.state.num}</div><button onClick={this.handleClick}>button</button></div>)}
}
export default App
  • 这时候我们再运行,首次还是会打印render() has been executed,说明render首次渲染,这是正常的情况
  • 然后我们点击button,发现render() has been executed没有再被打印,说明我们通过shouldComponentUpdate已经阻止了本次render渲染,应为状态没有发生变化
  • 这时候我们改变一下handleClick方式,让发state状态发生变化,看render会不会执行
import React from 'react'
class App extends React.Component{constructor(props){super(props)this.state={num:0}}shouldComponentUpdate(nextProps,nextState,nextContent){//nextProps表示即将接受的新的props//nextState表示即将接受的心得State,//这里我们通过判断nextState.num === this.state.num,如果是返回false,阻止render执行;如果true,render继续渲染if(nextState.num ===  this.state.num){return false }else{return true}}handleClick=()=>{this.setState({num:this.state.num+1})}render(){console.log('render() has been executed')return(<div><div>{this.state.num}</div><button onClick={this.handleClick}>button</button></div>)}
}
export default App
  • 运行,点击button发现打印render() has been executed,说明render执行渲染了,因为state发生变化了
  • 如此,shouldComponentUpdate逻辑验证完毕

2.2 上父子组件,验证props

  • 直接上代码
import React from 'react'
//定义子组件
class Child extends React.Component{constructor(props){super(props)}render(){console.log(' Child render() has been executed')return (<div>childNum :{this.props.childNum}</div>)}
}
class App extends React.Component{constructor(props){super(props)this.state={num:0,childNum:0}}   handleClick=()=>{this.setState({num:this.state.num+1})}render(){return(<div><div>num :{this.state.num}</div><Child childNum={this.state.childNum}></Child><button onClick={this.handleClick}>button</button></div>)}
}
export default App
  • 我们运行,首次子组件render会执行,打印Child render() has been executed
    在这里插入图片描述

  • 然后我们点击父组件的button给num自增1,这时候父组件因为state发生变化了,要执行 render,但是传递给子组件的props并没有发生变化,我们没有对ChildNum做任何处理

  • 但是我们会发现,点击button的时候打印了Child render() has been executed,说明子组件的render执行了,子组件被重新渲染了,这不是我们想要的

  • 我们希望props不发生变化的时候,子组件不执行render,这首shouldComponentUpdate该上场了

import React from 'react'
//定义子组件
class Child extends React.Component{constructor(props){super(props)}shouldComponentUpdate(nextProps,nextState,nextContext){//nextProps表示即将接受的新的props//nextState表示即将接受的心得State,//这里我们通过判断nextState.num === this.state.num,如果是返回false,阻止render执行;如果true,render继续渲染if(nextProps.childNum === this.props.childNum){return false}else{return true}}render(){console.log(' Child render() has been executed')return (<div>childNum :{this.props.childNum}</div>)}
}
class App extends React.Component{constructor(props){super(props)this.state={num:0,childNum:0}}   handleClick=()=>{this.setState({num:this.state.num+1})}render(){return(<div><div>num :{this.state.num}</div><Child childNum={this.state.childNum}></Child><button onClick={this.handleClick}>button</button></div>)}
}
export default App
  • 我们再运行,点击button,发现子组件的render方法并没有执行,因为props没有发生变化
  • 这时候我们改变一下父组件的handleClick方法,让childNum也变化一下,看子组件render会不会执行
import React from 'react'
//定义子组件
class Child extends React.Component{constructor(props){super(props)}shouldComponentUpdate(nextProps,nextState,nextContext){//nextProps表示即将接受的新的props//nextState表示即将接受的心得State,//这里我们通过判断nextState.num === this.state.num,如果是返回false,阻止render执行;如果true,render继续渲染if(nextProps.childNum === this.props.childNum){return false}else{return true}}render(){console.log(' Child render() has been executed')return (<div>childNum :{this.props.childNum}</div>)}
}
class App extends React.Component{constructor(props){super(props)this.state={num:0,childNum:0}}   handleClick=()=>{this.setState({num:this.state.num+1,childNum:this.state.childNum+1//让子组件的props也发生变化})}render(){return(<div><div>num :{this.state.num}</div><Child childNum={this.state.childNum}></Child><button onClick={this.handleClick}>button</button></div>)}
}
export default App
  • 运行,点击button,发现子组件的render又被执行了,完美,props验证完毕

2. PureComponent

2.1 单组件验证state

  • 上面我们通过在shouldComponent先state和props的判断逻辑,决定要不要执行render,这样的问题在于每次都要写,就比较麻烦,如果React能提供一套内置的比较逻辑,这样我们就不用每次写了,有没有这样的功能呢,有,那就是PureComponent
  • PureComponent通过浅比较props和state自动实现shouldComponentUpdate,从而决定render是否执行,他是React.Component的一个变体,我们直接实现继承就可以实现
  • 浅比较比较好理解吧,不懂的自行研究一下,因为是浅比较,所以PureComponent适合一下数据结构简单扁平的基本类型
  • 话不多说上代码
import React from 'react'
class App extends React.PureComponent{constructor(props){super(props)this.state={num:0}}handleClick=()=>{this.setState({num:0})}render(){console.log('render() has been executed')return(<div><div>{this.state.num}</div><button onClick={this.handleClick}>button</button></div>)}
}
export default App
  • 运行,点击button,发现render不行执行,
  • 改变handleClick,再次验证
import React from 'react'
class App extends React.PureComponent{constructor(props){super(props)this.state={num:0}}handleClick=()=>{this.setState({num:this.state.num+1})}render(){console.log('render() has been executed')return(<div><div>{this.state.num}</div><button onClick={this.handleClick}>button</button></div>)}
}
export default App
  • 运行点击button,发现render被执行,验证完毕

2.2 父子组件验证props

  • 类比结合1.2和2.1,小伙伴们资兴市实现以下,不会的留言解答

3.React.memo

  • 上面我们讨论的都是类组件的state和props的变化产生的render性能优化
  • 针对函数组件,React提出了memo
  • memo的作用可以理解为只比较props的PureComponent,因为函数组件没有状态么
  • 上代码
import React from 'react'
const Child=function Child(props){console.log('child function has been executed')return (<div>num from props:{props.num}</div>)
}
function App(props){const [num,setNum]=React.useState(0)const [count,setCount]=React.useState(0)return(<div><div>count:{count}</div><Child num={num}></Child><button onClick={()=>{setCount(count=>count+1)}}>button</button></div>)
}
export default App
  • 运行,首次渲染子组件,打印child function has been executed
  • 点击button,打印child function has been executed,说明子组件又被重新渲染了,是因为虽然num的值没有变化,但是count的值改变导致父组件重新渲染,进一步导致子组件重新渲染
  • 这时候我们加个React.memo,让props不发生变化的情况下,子组件不被重新渲染
import React from 'react'
const Child=React.memo(function Child(props){console.log('child function has been executed')return (<div>num from props:{props.num}</div>)
})
function App(props){const [num,setNum]=React.useState(0)const [count,setCount]=React.useState(0)return(<div><div>count:{count}</div><Child num={num}></Child><button onClick={()=>{setCount(count=>count+1)}}>button</button></div>)
}
export default App
  • 运行,点击button,不打印,子组件不重新渲染
  • 然后改变父组件的num,让num也变化起来,看看子组件渲染情况
import React from 'react'
const Child=React.memo(function Child(props){console.log('child function has been executed')return (<div>num from props:{props.num}</div>)
})
function App(props){const [num,setNum]=React.useState(0)const [count,setCount]=React.useState(0)return(<div><div>count:{count}</div><Child num={num}></Child><button onClick={()=>{setCount(count=>count+1);setNum(num=>num+1)}}>button</button></div>)
}
export default App
  • 运行,点击button,子组件重新被渲染,验证完毕

4. 总结

  • 根据不同的业务场景和数据结构,选用不同的render性能优化方式
http://www.dtcms.com/a/285709.html

相关文章:

  • 在 React 中根据数值动态设置 SVG 线条粗细
  • Linux | Bash 子字符串提取
  • Java 8 Stream 流操作大全:从入门到实战全覆盖(附案例)
  • 【软件系统架构】系列七:系统性能——计算机性能深入解析
  • 【机器人】HOV-SG 开放词汇 | 分层3D场景图 | 语言引导机器人导航
  • DNN平台因恶意交互导致NTLM哈希泄露漏洞(CVE-2025-52488)
  • ​​Docker 速通教程
  • 用Python实现神经网络(四)
  • 【大模型:知识图谱】--6.Neo4j DeskTop安装+使用
  • 数字化转型:概念性名词浅谈(第三十讲)
  • JAVA面试宝典 -《Elasticsearch 深度调优实战》
  • OpenAI发布通用智能体ChatGPT Agent:实现自主思考、联网与工具调用,智能体赛道大变天!
  • Java 大视界 -- Java 大数据在智能医疗电子健康档案数据挖掘与健康服务创新中的应用(350)
  • QCustomPlot 使用教程
  • 【Linux】基本指令(入门篇)(上)
  • SOES:软实现EtherCAT从站协议栈项目介绍及从站开发案例
  • 【Python】SQLAlchemy实现upsert
  • 【Linux网络编程】应用层协议 - HTTP
  • uniapp 动态控制横屏(APP 端)
  • Bitbucket平台的HTTP Access Tokens操作手册
  • 开发避坑短篇(2):uni-app微信小程序开发‘createIndependentPlugin‘模块缺失问题分析与解决方案
  • 创蓝闪验SDK适配uniappx版本UTS插件集成文档
  • Redis缓存双写的学习(五)
  • Python暑期学习笔记5
  • 平板可以用来办公吗?从文档处理到创意创作的全面测评
  • JavaScript 的垃圾回收机制
  • 第 14 章 线性回归预测策略----SPL量化编程课
  • CUPED (Controlled-experiment using Pre-Experiment Data) 论文学习笔记
  • 软删除设计:为什么使用 deleted_at = ‘1970-01-01 00:00:00‘ 表示未删除?
  • 1-大语言模型—理论基础:详解Transformer架构的实现(1)