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

一文讲清楚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.xdnf.cn/news/15867.html

相关文章:

  • Android音视频探索之旅 | Webrtc 1对1音视频通话核心流程分析
  • 借助AI学习开源代码git0.7之三git-init-db
  • YOLO演变史(一)
  • CSS样式中的布局、字体、响应式布局
  • CMakeLists.txt 配置文件
  • 非线性优化相关库笔记
  • 【面试题】大厂高压面经实录丨第二期
  • @Qualifier(“beanName“) 详解
  • 一个逻辑问题
  • 《设计模式之禅》笔记摘录 - 8.命令模式
  • Day06_C语言网络编程20250718mobus重点
  • gin数据解析和绑定
  • 门控线性单元GLU (Gated Linear Unit)
  • Go语言流程控制(if / for)
  • 一小时学习Redis
  • websocket案例 599足球比分
  • 海森矩阵(Hessian Matrix)在SLAM图优化和点云配准中的应用介绍
  • 实战指南|智慧无人机安防系统搭建全流程解析
  • 深入理解Linux文件操作:stdin/stdout/stderr与C语言文件函数全解析
  • PDF 拆分合并PDFSam:开源免费 多文件合并 + 按页码拆分 本地处理
  • 突破性量子芯片问世:电子与光子首次集成,开启量子技术规模化应用新篇章
  • 暑期自学嵌入式——Day05补充(C语言阶段)
  • 二分答案之第 K 小/大
  • Visual Studio编译WPF项目生成的文件介绍
  • 服务器mysql数据的简单备份脚本
  • 二、Dify 版本升级教程(LInux-openeuler)
  • iOS OC 图片压缩
  • vue2 面试题及详细答案150道(101 - 120)
  • 国产替代:ASP4644在电信通信设备中的测试与应用前景
  • Java类:BigDecimal 的用法:乘法