React?模式之純組件使用示例詳解
什么是純組件
純組件(Pure Component)這概念衍生自純函數(shù)。純函數(shù)指的是返回結(jié)果只依賴(lài)于傳入的參數(shù),且對(duì)函數(shù)作用域外沒(méi)有副作用的函數(shù)。這種函數(shù)在相同參數(shù)下,返回結(jié)果是不變的。純函數(shù)的返回值能被安全地緩存起來(lái),在下次調(diào)用時(shí),跳過(guò)函數(shù)執(zhí)行,直接讀取緩存。因?yàn)楹瘮?shù)沒(méi)有外部副作用,不執(zhí)行函數(shù)對(duì)整個(gè)程序沒(méi)有影響。
與純函數(shù)類(lèi)似,如果一個(gè)組件在 props 和 state 相同的情況下,每次 render 的結(jié)果都是相同的,那這個(gè)組件就是純組件。也就是說(shuō),純組件的 render 結(jié)果只依賴(lài)于 props 和 state,如果兩次渲染中,props 和 state 是相同的,那它們的 render 結(jié)果也是一樣的。
純組件解決了什么問(wèn)題
在理解純組件有什么用處之前,先來(lái)看一下 React 如何更新組件。
React 在更新組件時(shí),會(huì)從觸發(fā)組件開(kāi)始,遞歸地調(diào)用整顆子樹(shù)的 render 函數(shù),構(gòu)建新的虛擬樹(shù)。即使子組件的 props 和 state 沒(méi)有變化,render 函數(shù)還是會(huì)被執(zhí)行。
看這個(gè)例子:
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,
}))
}
>
發(fā)送消息
</button>
<MessageText message={messageData.message} />
</div>
);
}
function MessageText(props) {
console.log("render in child");
return <div>最新消息:{props.message}</div>;
}
連續(xù)點(diǎn)擊幾次按鈕:

父組件 Messager 由于狀態(tài)更新,需要重新執(zhí)行 render 函數(shù)來(lái)更新組件,這個(gè)是符合預(yù)期的。子組件 MessageText 中,第一次更新,由于 message 屬性改變,也需要更新,這個(gè)也容易理解。
問(wèn)題在后面幾次更新:
- 子組件的 props 沒(méi)有變化,為什么執(zhí)行了 render 函數(shù)?
- render 函數(shù)執(zhí)行了,是不是意味著 DOM 也更新了,只是我們看不出變化?
- 組件在 props 沒(méi)有變化時(shí),繪制的視圖都是不變的,能不能跳過(guò) render 函數(shù)的執(zhí)行?
讓我一個(gè)一個(gè)來(lái)回答。
Q:為什么需要執(zhí)行 render 函數(shù)?
A:你的組件可能使用了任何變量,包括全局變量、環(huán)境變量等,React 沒(méi)有能力做到監(jiān)聽(tīng)這些變量,這些變量的變化是 React 無(wú)法感知的。為了保證渲染的視圖與數(shù)據(jù)是一致的,React 只能犧牲性能,在每次更新的時(shí)候,都去執(zhí)行 render 函數(shù),獲取最新的 render 輸出。
Q: 執(zhí)行了 render 函數(shù)就一定會(huì)更新 DOM 嗎?
A:不一定。render 函數(shù)的輸出結(jié)果是 React 虛擬 DOM,執(zhí)行了 render 函數(shù)會(huì)更新虛擬 DOM,但 React 足夠聰明,能夠比對(duì)出瀏覽器 DOM 需要更新的地方,讓瀏覽器只進(jìn)行必要的重繪。這也是 React 能夠保證性能的重要手段。
Q:能避免不必要的 render 執(zhí)行嗎?
A:可以。如果是 class 組件(CC),你可以重寫(xiě) shouldComponentUpdate() 方法或繼承 React.PureComponent。如果是函數(shù)組件(FC),你可以使用 React.memo() 。這樣能夠避免不必要的 render 執(zhí)行,在一定程度上提升頁(yè)面的性能,尤其是當(dāng) render 函數(shù)內(nèi)有復(fù)雜計(jì)算時(shí)。這也正是純組件想要解決的問(wèn)題。
怎么使用純組件
CC: shouldComponentUpdate() 和 React.PureComponent
?? 這一小節(jié)的內(nèi)容基于 class 組件,F(xiàn)C 不適用。
React 組件更新前,會(huì)調(diào)用 shouldComponentUpdate(nextProps,nextState),當(dāng)返回 true 時(shí),組件就會(huì) re-render。所以,你可以重寫(xiě)這個(gè)方法,當(dāng)不希望組件更新時(shí),返回 false。
重寫(xiě)上面的 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 函數(shù)只會(huì)在 props.message 變化的時(shí)候才會(huì)被調(diào)用。當(dāng)然,你可以自己決定哪些條件下跳過(guò) render。
?? shouldComponentUpdate 返回 false 時(shí)并不能保證跳過(guò) render。React 后續(xù)可能會(huì)增加自己的判斷,只把這個(gè)返回結(jié)果作為一種提示。所以這個(gè)方法應(yīng)該只能被用于性能優(yōu)化,不能作為邏輯依賴(lài)。
大部分時(shí)候,我們期望在 props 和 state 不變的時(shí)候,跳過(guò) render,因?yàn)檫@經(jīng)常導(dǎo)致不必要的更新。上面的例子只有一個(gè)屬性,有點(diǎn)過(guò)于簡(jiǎn)單了,組件可能會(huì)多個(gè) props 和 state,需要在 () 中窮舉比較。
因?yàn)檫@種模式太過(guò)常見(jiàn),React 提供了 React.PureComponent 類(lèi),你可以繼承這個(gè)類(lèi),來(lái)實(shí)現(xiàn)純組件的效果,即當(dāng) props 和 state 不變(淺比較)時(shí),跳過(guò) 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()
先回答一個(gè)普遍疑惑的問(wèn)題。
Q:FC 是純組件嗎?或者無(wú)狀態(tài)的 FC 是純組件嗎? A:并不是。從最上面的例子就可以看出來(lái)。無(wú)狀態(tài) FC 與純組件是獨(dú)立的概念,狀態(tài)并不是影響純組件的因素,關(guān)鍵在于組件函數(shù)除了 state 和 props 有沒(méi)有外部依賴(lài),對(duì)外部有沒(méi)有影響。
Q:既然如此,怎么把 FC 改造成純組件? A:很簡(jiǎn)單,用 class 重寫(xiě)組件并繼承 React.PureComponent 就可以了。
說(shuō)笑了,這年頭,誰(shuí)寫(xiě) React 還用 class 啊。
然而,很遺憾,hooks 無(wú)法覆蓋 shouldComponentUpdate() 的使用場(chǎng)景,F(xiàn)C 沒(méi)有等效于 React.PureComponent 的寫(xiě)法。
不過(guò),倒是可以使用 React.memo() 實(shí)現(xiàn)一個(gè)半吊子的純組件。
const memorizedFC=React.memo(FC,arePropsEqual(preProps,nextProps)=>{
// 返回true,跳過(guò)render
// 返回false,執(zhí)行render
})
React.memo() 把上次調(diào)用的結(jié)果保存在內(nèi)存中,下次調(diào)用時(shí),如果 arePropsEqual() 返回 true,那就直接使用上次的結(jié)果,不需要執(zhí)行 FC。arePropsEqual 參數(shù)可選,默認(rèn)使用淺比較。
利用 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,對(duì)于狀態(tài)導(dǎo)致的更新,F(xiàn)C 依然會(huì)執(zhí)行。這也是為什么說(shuō)是“半吊子純組件”。
如果 FC 無(wú)狀態(tài),那 React.memo() 就可以等效于 React.PureComponent 了。既然如此,對(duì)有狀態(tài) FC,可以利用狀態(tài)上移把 state 轉(zhuǎn)為 props,再應(yīng)用 React.memo(), 實(shí)現(xiàn)純組件的效果。
所以,絕大多數(shù)情況下,React.memo() 已經(jīng)足夠了。
你可能并不需要純組件
了解純組件的概念,以及它對(duì) React 應(yīng)用性能的影響,對(duì)一個(gè)開(kāi)發(fā)者有很大幫助,但這并不意味著你需要經(jīng)常使用它。
React 提供了良好的性能保證,大部分情況下,你的應(yīng)用不會(huì)有性能上的問(wèn)題,使用純組件反而增加理解成本。
即使出現(xiàn)了性能問(wèn)題,一些通用的性能優(yōu)化手段可能更有效果。只有當(dāng)性能瓶頸出現(xiàn)在特定組件的 render,并且這個(gè)組件可以被改造成純組件時(shí),這個(gè)措施才會(huì)有效果。
以上就是React 模式之純組件使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于React 純組件模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React/Redux應(yīng)用使用Async/Await的方法
本篇文章主要介紹了React/Redux應(yīng)用使用Async/Await的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
React hooks如何清除定時(shí)器并驗(yàn)證效果
在React中,通過(guò)自定義Hook useTimeHook實(shí)現(xiàn)定時(shí)器的啟動(dòng)與清除,在App組件中使用Clock組件展示當(dāng)前時(shí)間,利用useEffect鉤子在組件掛載時(shí)啟動(dòng)定時(shí)器,同時(shí)確保組件卸載時(shí)清除定時(shí)器,避免內(nèi)存泄露,這種方式簡(jiǎn)化了狀態(tài)管理和副作用的處理2024-10-10
React學(xué)習(xí)之事件綁定的幾種方法對(duì)比
這篇文章主要給大家介紹了關(guān)于React學(xué)習(xí)之事件綁定的幾種方法對(duì)比,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09
React組件實(shí)例三大核心屬性State props Refs詳解
組件實(shí)例的三大核心屬性是:State、Props、Refs。類(lèi)組件中這三大屬性都存在。函數(shù)式組件中訪問(wèn)不到 this,也就不存在組件實(shí)例這種說(shuō)法,但由于它的特殊性(函數(shù)可以接收參數(shù)),所以存在Props這種屬性2022-12-12
React版本18.xx降低為17.xx的方法實(shí)現(xiàn)
由于現(xiàn)在react默認(rèn)創(chuàng)建是18.xx版本,但是我們現(xiàn)在大多使用的還是17.xx或者更低的版本,于是要對(duì)react版本進(jìn)行降級(jí),本文主要介紹了React版本18.xx降低為17.xx的方法實(shí)現(xiàn),感興趣的可以了解一下2023-11-11

