網站首頁 編程語言 正文
什么是純組件
純組件(Pure Component)這概念衍生自純函數。純函數指的是返回結果只依賴于傳入的參數,且對函數作用域外沒有副作用的函數。這種函數在相同參數下,返回結果是不變的。純函數的返回值能被安全地緩存起來,在下次調用時,跳過函數執行,直接讀取緩存。因為函數沒有外部副作用,不執行函數對整個程序沒有影響。
與純函數類似,如果一個組件在 props 和 state 相同的情況下,每次 render 的結果都是相同的,那這個組件就是純組件。也就是說,純組件的 render 結果只依賴于 props 和 state,如果兩次渲染中,props 和 state 是相同的,那它們的 render 結果也是一樣的。
純組件解決了什么問題
在理解純組件有什么用處之前,先來看一下 React 如何更新組件。
React 在更新組件時,會從觸發組件開始,遞歸地調用整顆子樹的 render 函數,構建新的虛擬樹。即使子組件的 props 和 state 沒有變化,render 函數還是會被執行。
看這個例子:
const defaultMessage = "Hello!"; function Messager() { console.log("render in parent"); const [messageInput, setMessageInput] = useState(defaultMessage); const [messageData, setMessageData] = useState({ id: 0, message: "", }); return ( <div className="logger"> <input value={messageInput} onChange={(e) => setMessageInput(e.target.value)} ></input> <button onClick={() => setMessageData((preData) => ({ id: preData.id + 1, message: messageInput, })) } > 發送消息 </button> <MessageText message={messageData.message} /> </div> ); } function MessageText(props) { console.log("render in child"); return <div>最新消息:{props.message}</div>; }
連續點擊幾次按鈕:
父組件 Messager
由于狀態更新,需要重新執行 render 函數來更新組件,這個是符合預期的。子組件 MessageText
中,第一次更新,由于 message
屬性改變,也需要更新,這個也容易理解。
問題在后面幾次更新:
- 子組件的 props 沒有變化,為什么執行了 render 函數?
- render 函數執行了,是不是意味著 DOM 也更新了,只是我們看不出變化?
- 組件在 props 沒有變化時,繪制的視圖都是不變的,能不能跳過 render 函數的執行?
讓我一個一個來回答。
Q:為什么需要執行 render 函數?
A:你的組件可能使用了任何變量,包括全局變量、環境變量等,React 沒有能力做到監聽這些變量,這些變量的變化是 React 無法感知的。為了保證渲染的視圖與數據是一致的,React 只能犧牲性能,在每次更新的時候,都去執行 render 函數,獲取最新的 render 輸出。
Q: 執行了 render 函數就一定會更新 DOM 嗎?
A:不一定。render 函數的輸出結果是 React 虛擬 DOM,執行了 render 函數會更新虛擬 DOM,但 React 足夠聰明,能夠比對出瀏覽器 DOM 需要更新的地方,讓瀏覽器只進行必要的重繪。這也是 React 能夠保證性能的重要手段。
Q:能避免不必要的 render 執行嗎?
A:可以。如果是 class 組件(CC),你可以重寫 shouldComponentUpdate()
方法或繼承 React.PureComponent
。如果是函數組件(FC),你可以使用 React.memo()
。這樣能夠避免不必要的 render 執行,在一定程度上提升頁面的性能,尤其是當 render 函數內有復雜計算時。這也正是純組件想要解決的問題。
怎么使用純組件
CC: shouldComponentUpdate() 和 React.PureComponent
? 這一小節的內容基于 class 組件,FC 不適用。
React 組件更新前,會調用 shouldComponentUpdate(nextProps,nextState)
,當返回 true
時,組件就會 re-render。所以,你可以重寫這個方法,當不希望組件更新時,返回 false
。
重寫上面的 MessageText
組件:
class MessageText extends React.Component { shouldComponentUpdate(nextProps) { if (nextProps.message === this.props.message) { return false; } return true; } render() { console.log("render in child with message=" + this.props.message); return <div>最新消息:{this.props.message}</div>; } }
這樣,render
函數只會在 props.message
變化的時候才會被調用。當然,你可以自己決定哪些條件下跳過 render。
? shouldComponentUpdate 返回 false 時并不能保證跳過 render。React 后續可能會增加自己的判斷,只把這個返回結果作為一種提示。所以這個方法應該只能被用于性能優化,不能作為邏輯依賴。
大部分時候,我們期望在 props 和 state 不變的時候,跳過 render,因為這經常導致不必要的更新。上面的例子只有一個屬性,有點過于簡單了,組件可能會多個 props 和 state,需要在 ()
中窮舉比較。
因為這種模式太過常見,React 提供了 React.PureComponent
類,你可以繼承這個類,來實現純組件的效果,即當 props 和 state 不變(淺比較)時,跳過 render。
class MessageText extends React.PureComponent { render() { console.log("render in child with message=" + this.props.message); return <div>最新消息:{this.props.message}</div>; } }
FC: React.memo()
先回答一個普遍疑惑的問題。
Q:FC 是純組件嗎?或者無狀態的 FC 是純組件嗎? A:并不是。從最上面的例子就可以看出來。無狀態 FC 與純組件是獨立的概念,狀態并不是影響純組件的因素,關鍵在于組件函數除了 state 和 props 有沒有外部依賴,對外部有沒有影響。
Q:既然如此,怎么把 FC 改造成純組件? A:很簡單,用 class 重寫組件并繼承 React.PureComponent
就可以了。
說笑了,這年頭,誰寫 React 還用 class 啊。
然而,很遺憾,hooks 無法覆蓋 shouldComponentUpdate()
的使用場景,FC 沒有等效于 React.PureComponent
的寫法。
不過,倒是可以使用 React.memo()
實現一個半吊子的純組件。
const memorizedFC=React.memo(FC,arePropsEqual(preProps,nextProps)=>{ // 返回true,跳過render // 返回false,執行render })
React.memo()
把上次調用的結果保存在內存中,下次調用時,如果 arePropsEqual()
返回 true
,那就直接使用上次的結果,不需要執行 FC。arePropsEqual
參數可選,默認使用淺比較。
利用 React.memo()
, 把 MessageText
改造成純組件 PureMessageText
:
function MessageText(props) { console.log("render in child with message=" + props.message); return <div>最新消息:{props.message}</div>; } const PureMessageText = React.memo(MessageText);
注意 React.memo()
并不等效于 React.PureComponent
,前者只能比較 props,對于狀態導致的更新,FC 依然會執行。這也是為什么說是“半吊子純組件”。
如果 FC 無狀態,那 React.memo()
就可以等效于 React.PureComponent
了。既然如此,對有狀態 FC,可以利用狀態上移把 state 轉為 props,再應用 React.memo()
, 實現純組件的效果。
所以,絕大多數情況下,React.memo()
已經足夠了。
你可能并不需要純組件
了解純組件的概念,以及它對 React 應用性能的影響,對一個開發者有很大幫助,但這并不意味著你需要經常使用它。
React 提供了良好的性能保證,大部分情況下,你的應用不會有性能上的問題,使用純組件反而增加理解成本。
即使出現了性能問題,一些通用的性能優化手段可能更有效果。只有當性能瓶頸出現在特定組件的 render,并且這個組件可以被改造成純組件時,這個措施才會有效果。
原文鏈接:https://juejin.cn/post/7135394374208192543
相關推薦
- 2022-05-31 如何使用yolov5輸出檢測到的目標坐標信息_python
- 2022-07-21 C#?PDF轉圖片(JPG,Png)的項目實踐_C#教程
- 2022-02-25 image-webpack-loader 報錯 Invalid regular expression
- 2022-06-20 一文帶你掌握Go語言運算符的使用_Golang
- 2022-07-28 Redis內存碎片處理實例詳解_Redis
- 2022-06-13 Docker四種網絡模式演示及連通性測試_docker
- 2022-11-14 值類型和引用類型的區別 I 數據結構中的堆和棧和內存中的堆和棧的區別
- 2022-03-04 如何在uni-app中選擇一個合適的UI組件庫
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支