一文講清楚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.pswp.cn/pingmian/89548.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89548.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89548.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

物聯網iot、mqtt協議與華為云平臺的綜合實踐(萬字0基礎保姆級教程)

本學期的物聯網技術與應用課程&#xff0c;其結課設計內容包含&#xff1a;mqtt、華為云、PyQT5和MySQL等結合使用&#xff0c;完成了從華為云配置產品信息以及轉發規則&#xff0c;到mqtt命令轉發&#xff0c;再到python編寫邏輯代碼實現相關功能&#xff0c;最后用PyQT5實現面…

使用IntelliJ IDEA和Maven搭建SpringBoot集成Fastjson項目

使用IntelliJ IDEA和Maven搭建SpringBoot集成Fastjson項目 下面我將詳細介紹如何在IntelliJ IDEA中使用Maven搭建一個集成Fastjson的SpringBoot項目&#xff0c;包含完整的環境配置和代碼實現。 一、環境準備 軟件要求 IntelliJ IDEA 2021.x或更高版本JDK 1.8或更高版本&#x…

Java從入門到精通!第九天, 重點!(集合(一))

十一、集合1. 為什么要使用集合(1) 數組存在的弊端1) 數組在初始化之后&#xff0c;長度就不能改變&#xff0c;不方便擴展。2) 數組中提供的屬性和方法比較少&#xff0c;不便于進行添加、刪除、修改等操作&#xff0c;并且效率不高&#xff0c;同時無法直接存儲元素的個數。3…

為什么使用時序數據庫

為什么使用時序數據庫&#xff1f; 時序數據庫&#xff08;Time-Series Database, TSDB&#xff09;是專為時間序列數據優化的數據庫&#xff0c;相比傳統關系型數據庫&#xff08;如MySQL&#xff09;或NoSQL數據庫&#xff08;如MongoDB&#xff09;&#xff0c;它在以下方面…

計算機網絡:(十一)多協議標記交換 MPLS

計算機網絡&#xff1a;&#xff08;十一&#xff09;多協議標記交換 MPLS前言一、傳統網絡的問題二、MPLS&#xff1a;給數據包貼個“標簽”三、MPLS的工作流程1. 入站2. 中間3. 出站四、MPLS的能力前言 前面我們講解了計算機網絡中網絡層的相關知識&#xff0c;包括網絡層轉發…

docker run elasticsearch 報錯

谷粒商城 p103 前提條件&#xff1a; 下載鏡像文件 #存儲和檢索數據 docker pull elasticsearch:7.4.2 #可視化檢索數據 docker pull kibana:7.4.2 創建掛載的文件和配置 mkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data echo "http.h…

巧用Callbre RVE生成DRC HTML report及CTO的使用方法

對于后端版圖人員&#xff0c;在芯片TO前的LV signoff階段&#xff0c;猶如一段漫長而有期待的朝圣之旅&#xff0c;需要耐心&#xff0c;毅力和信心&#xff0c;在龐雜的DRC中找到一條收斂之路。為了讓此路更為清晰收斂&#xff0c;Calibre提供了一套可追溯對比的富文本方式-H…

產品需求文檔(PRD)格式全解析:從 RP 到 Word 的選擇與實踐

產品需求文檔&#xff08;PRD&#xff09;的形式多種多樣&#xff0c;但核心目標始終一致&#xff1a;清晰傳遞產品需求&#xff0c;讓團隊高效協作。不同公司對 PRD 的格式要求可能不同&#xff0c;有的偏愛直接在原型工具中撰寫&#xff0c;有的則習慣用 Word 整理歸檔。本文…

【C++】入門階段

一、初始化C中的初始化指為變量賦予初始值的過程。初始化方式多樣&#xff0c;適用于不同場景。char cha0; char chb{0}; char chc(\0); char chdcha; char che{};注意事項優先使用列表初始化&#xff08;{}&#xff09;&#xff0c;避免窄化轉換風險。在c11中{ }在變量&#x…

tailscale在ubuntu22.04上使用

支持 x86 和 ARM 架構 CPU 的軟件包已提供 32 位和 64 位版本。 添加 Tailscale 的軟件包簽名密鑰及倉庫&#xff1a; curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null c…

深入解析Linux文件重定向原理與dup2系統調用

在Linux中&#xff0c;重定向&#xff08;Redirection&#xff09;是一種強大的功能&#xff0c;允許用戶控制命令的輸入來源&#xff08;stdin&#xff09;和輸出目標&#xff08;stdout和stderr&#xff09;。通過重定向&#xff0c;你可以將命令的輸出保存到文件、從文件讀取…

QGIS制作的儀表盤工程

在QGIS的官方資源庫下載了一個QGIS制作的儀表盤工程&#xff0c;感覺非常炫酷&#xff01;分享給大家&#xff01;下面的儀表盤會將選中的道路數及長度&#xff0c;動態顯示在相應的儀表項中&#xff01;下面的儀表盤會將選中的道路數及長度&#xff0c;動態顯示在相應的儀表項…

Python高級數據類型:集合(Set)

集合是Python中一種非常有用的數據結構&#xff0c;它與列表類似但具有獨特的特性。本文將全面介紹集合的所有知識點&#xff0c;從基礎概念到高級用法&#xff0c;幫助初學者徹底掌握集合的使用。1. 集合簡介1.1 什么是集合&#xff1f;集合&#xff08;Set&#xff09;是Pyth…

【Unity編輯器開發GUI.Window】

Unity GUI.Window 筆記 根據官方文檔2021版本的&#xff0c;點擊鏈接跳轉記錄 概述 GUI.Window 是 Unity IMGUI 系統中用于創建彈出窗口的核心方法&#xff0c;具有以下關鍵特性&#xff1a; 浮動窗口&#xff1a;浮于普通 GUI 控件之上焦點控制&#xff1a;可通過點擊獲得焦…

CAN通信驅動開發注意事項

以下是CAN通信驅動開發的關鍵注意事項相關的整理,涵蓋硬件配置、協議實現、錯誤處理及性能優化等方面: 一、硬件層配置要點 引腳復用與時鐘 確認MCU的CAN控制器引腳是否與GPIO復用,正確配置復用模式。 檢查CAN控制器時鐘源,確保波特率計算基準準確。 收發器(Transceiver)…

CCF編程能力等級認證GESP—C++8級—20250628

CCF編程能力等級認證GESP—C8級—20250628單選題&#xff08;每題 2 分&#xff0c;共 30 分&#xff09;判斷題&#xff08;每題 2 分&#xff0c;共 20 分&#xff09;編程題 (每題 25 分&#xff0c;共 50 分)樹上旅行遍歷計數單選題&#xff08;每題 2 分&#xff0c;共 30…

135. Java 泛型 - 無界通配符

文章目錄135. Java 泛型 - 無界通配符 (?)**1. 什么是無界通配符 (?)&#xff1f;****2. 為什么使用無界通配符&#xff1f;****3. 示例&#xff1a;使用 ? 處理任意列表****? 錯誤示例****? 正確示例****4. 為什么 List<Object> 和 List<?> 不一樣&#xff…

NOIP提高組|2010T1機器翻譯

NOIP2010年提高組第一題:機器翻譯 題目描述 小晨的電腦上安裝了一個機器翻譯軟件,他經常用這個軟件來翻譯英語文章。 這個翻譯軟件的原理很簡單,它只是從頭到尾,依次將每個英文單詞用對應的中文含義來替換。對于每個英文單詞,軟件會先在內存中查找這個單詞的中文含義,如果…

Change Data Capture (CDC) with Kafka Connect:實時數據同步的完整指南

Change Data Capture (CDC) 是一種高效的數據同步技術&#xff0c;能夠捕獲數據庫的變更&#xff08;插入、更新、刪除&#xff09;并實時傳輸到其他系統。結合 Kafka Connect&#xff0c;我們可以構建一個可靠、可擴展的 CDC 管道&#xff0c;實現數據庫與數據湖、數據倉庫或消…

云手機網絡加速全攻略:解決游戲卡頓與APP連接失敗困擾

用云手機玩游戲、掛腳本、跑自動任務&#xff0c;明明后臺顯示在線&#xff0c;但畫面卡頓、操作延遲、甚至APP直接“轉圈圈連不上”&#xff0c;是不是很抓狂&#xff1f;問題出在哪里&#xff1f;云手機不卡&#xff0c;網絡卡&#xff1f;其實&#xff0c;大多數云手機的性能…