詳解使用React.memo()來優(yōu)化函數(shù)組件的性能
React核心開發(fā)團隊一直都努力地讓React變得更快。在React中可以用來優(yōu)化組件性能的方法大概有以下幾種:
- 組件懶加載(React.lazy(...)和<Suspense />)
- Pure Component
- shouldComponentUpdate(...){...}生命周期函數(shù)
本文還會介紹React16.6加入的另外一個專門用來優(yōu)化函數(shù)組件(Functional Component)性能的方法: React.memo。
無用的渲染
組件是構(gòu)成React視圖的一個基本單元。有些組件會有自己本地的狀態(tài)(state), 當它們的值由于用戶的操作而發(fā)生改變時,組件就會重新渲染。在一個React應用中,一個組件可能會被頻繁地進行渲染。這些渲染雖然有一小部分是必須的,不過大多數(shù)都是無用的,它們的存在會大大降低我們應用的性能。
看下面這個例子:
import React from 'react';
class TestC extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate')
}
render() {
return (
<div >
{this.state.count}
<button onClick={()=>this.setState({count: 1})}>Click Me</button>
</div>
);
}
}
export default TestC;
TestC組件有一個本地狀態(tài)count,它的初始值是0(state = {count: 0})。當我們點擊Click Me按鈕時,count的值被設置為1。這時候屏幕的數(shù)字將會由0變成1。當我們再次點擊該按鈕時,count的值還是1, 這時候TestC組件不應該被重新渲染,可是現(xiàn)實是這樣的嗎?
為了測試count重復設置相同的值組件會不會被重新渲染, 我為TestC組件添加了兩個生命周期函數(shù): componentWillUpdate和componentDidUpdate。componentWillUpdate方法在組件將要被重新渲染時被調(diào)用,而componentDidUpdate方法會在組件成功重渲染后被調(diào)用。
在瀏覽器中運行我們的代碼,然后多次點擊Click Me按鈕,你可以看到以下輸出:

我們可以看到'componentWillUpdate'和'componentWillUpdate'在每次我們點擊完按鈕后,都會在控制臺輸出來。所以即使count被設置相同的值,TestC組件還是會被重新渲染,這些就是所謂的無用渲染。
Pure Component/shouldComponentUpdate
為了避免React組件的無用渲染,我們可以實現(xiàn)自己的shouldComponentUpdate生命周期函數(shù)。
當React想要渲染一個組件的時候,它將會調(diào)用這個組件的shouldComponentUpdate函數(shù), 這個函數(shù)會告訴它是不是真的要渲染這個組件。
如果我們的shouldComponentUpdate函數(shù)這樣寫:
shouldComponentUpdate(nextProps, nextState) {
return true
}
其中各個參數(shù)的含義是:
nextProps: 組件將會接收的下一個參數(shù)propsnextProps: 組件的下一個狀態(tài)state
因為我們的shouldComponentUpdate函數(shù)一直返回true,這就告訴React,無論何種情況都要重新渲染該組件。
可是如果我們這么寫:
shouldComponentUpdate(nextProps, nextState) {
return false
}
因為這個方法的返回值是false,所以React永遠都不會重新渲染我們的組件。
因此當你想要React重新渲染你的組件的時候,就在這個方法中返回true,否則返回false?,F(xiàn)在讓我們用shouldComponentUpdate重寫之前的TestC組件:
import React from 'react';
class TestC extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate')
}
shouldComponentUpdate(nextProps, nextState) {
if (this.state.count === nextState.count) {
return false
}
return true
}
render() {
return (
<div>
{ this.state.count }
<button onClick = {
() => this.setState({ count: 1 }) }> Click Me </button>
</div>
);
}
}
export default TestC;
我們在TestC組件里添加了shouldComponentUpdate方法,判斷如果現(xiàn)在狀態(tài)的count和下一個狀態(tài)的count一樣時,我們返回false,這樣React將不會進行組件的重新渲染,反之,如果它們兩個的值不一樣,就返回true,這樣組件將會重新進行渲染。
再次在瀏覽器中測試我們的組件,剛開始的界面是這樣的:

這時候,就算我們多次點擊Click Me按鈕,也只能看到兩行輸出:
componentWillUpdate
componentDidUpdate
因為第二次點擊Click Me按鈕后count值一直是1,這樣shouldComponentUpdate一直返回false,所以組件就不再被重新渲染了。
那么如何驗證后面state的值發(fā)生改變,組件還是會被重新渲染呢?我們可以在瀏覽器的React DevTools插件中直接對TestC組件的狀態(tài)進行更改。具體做法是, 在Chrome調(diào)試工具中點擊React標簽,在界面左邊選中TestC組件,在界面的右邊就可以看到其狀態(tài)state中只有一個鍵count,且其值是1:

然后讓我們點擊count的值1,將其修改為2,然后按回車鍵:

你將會看到控制臺有以下輸出:
componentWillUpdate
componentDidUpdate
componentWillUpdate
componentDidUpdate
state的count被改變了,組件也被重新渲染了。
現(xiàn)在讓我們使用另外一種方法PureComponent來對組件進行優(yōu)化。
React在v15.5的時候引入了Pure Component組件。React在進行組件更新時,如果發(fā)現(xiàn)這個組件是一個PureComponent,它會將組件現(xiàn)在的state和props和其下一個state和props進行淺比較,如果它們的值沒有變化,就不會進行更新。要想讓你的組件成為Pure Component,只需要extends React.PureComponent即可。
讓我們用PureComponent去改寫一下我們的代碼吧:
import React from 'react';
class TestC extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentWillUpdate(nextProps, nextState) {
console.log('componentWillUpdate')
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate')
}
/*shouldComponentUpdate(nextProps, nextState) {
if (this.state.count === nextState.count) {
return false
}
return true
}*/
render() {
return (
<div>
{ this.state.count }
<button onClick = {
() => this.setState({ count: 1 })
}> Click Me </button>
</div >
);
}
}
export default TestC;
在上面的代碼中,我將shouldComponentUpdate的代碼注釋掉了,因為React.PureComponent本身就幫我們實現(xiàn)了一樣的功能。
改完代碼后,我們刷新一下瀏覽器,然后多次點擊Click Me按鈕看組件被渲染了多少遍:

由上面的輸出可知,我們的component只在state由0變?yōu)?時被重新渲染了,后面都沒有進行渲染。
函數(shù)組件
上面我們探討了如何使用PureComponent和shouldComponentUpdate的方法優(yōu)化類組件的性能。雖然類組件是React應用的主要組成部分,不過函數(shù)組件(Functional Component)同樣可以被作為React組件使用。
function TestC(props) {
return (
<div>
I am a functional component
</div>
)
}
對于函數(shù)組件,它們沒有諸如state的東西去保存它們本地的狀態(tài)(雖然在React Hooks中函數(shù)組件可以使用useState去使用狀態(tài)), 所以我們不能像在類組件中使用shouldComponentUpdate等生命函數(shù)去控制函數(shù)組件的重渲染。當然,我們也不能使用extends React.PureComponent了,因為它壓根就不是一個類。
要探討解決方案,讓我們先驗證一下函數(shù)組件是不是也有和類組件一樣的無用渲染的問題。
首先我們先將ES6的TestC類轉(zhuǎn)換為一個函數(shù)組件:
import React from 'react';
const TestC = (props) => {
console.log(`Rendering TestC :` props)
return (
<div>
{props.count}
</div>
)
}
export default TestC;
// App.js
<TestC count={5} />
當上面的代碼初次加載時,控制臺的輸出是:

同樣,我們可以打開Chrome的調(diào)試工具,點擊React標簽然后選中TestC組件:

我們可以看到這個組件的參數(shù)值是5,讓我們將這個值改為45, 這時候瀏覽器輸出:

由于count的值改變了,所以該組件也被重新渲染了,控制臺輸出Object{count: 45},讓我們重復設置count的值為45, 然后再看一下控制臺的輸出結(jié)果:

由輸出結(jié)果可以看出,即使count的值保持不變,還是45, 該組件還是被重渲染了。
既然函數(shù)組件也有無用渲染的問題,我們?nèi)绾螌ζ溥M行優(yōu)化呢?
解決方案: 使用React.memo()
React.memo(...)是React v16.6引進來的新屬性。它的作用和React.PureComponent類似,是用來控制函數(shù)組件的重新渲染的。React.memo(...) 其實就是函數(shù)組件的React.PureComponent。
如何使用React.memo(...)?
React.memo使用起來非常簡單,假設你有以下的函數(shù)組件:
const Funcomponent = ()=> {
return (
<div>
Hiya!! I am a Funtional component
</div>
)
}
我們只需將上面的Funcomponent作為參數(shù)傳入React.memo中:
const Funcomponent = ()=> {
return (
<div>
Hiya!! I am a Funtional component
</div>
)
}
const MemodFuncComponent = React.memo(FunComponent)
React.memo會返回一個純化(purified)的組件MemoFuncComponent,這個組件將會在JSX標記中渲染出來。當組件的參數(shù)props和狀態(tài)state發(fā)生改變時,React將會檢查前一個狀態(tài)和參數(shù)是否和下一個狀態(tài)和參數(shù)是否相同,如果相同,組件將不會被渲染,如果不同,組件將會被重新渲染。
現(xiàn)在讓我們在TestC組件上使用React.memo進行優(yōu)化:
let TestC = (props) => {
console.log('Rendering TestC :', props)
return (
<div>
{ props.count }
</>
)
}
TestC = React.memo(TestC);
打開瀏覽器重新加載我們的應用。然后打開Chrome調(diào)試工具,點擊React標簽,然后選中<Memo(TestC)>組件。
接著編輯一下props的值,將count改為89,我們將會看到我們的應用被重新渲染了:

然后重復設置count的值為89:

這里沒有重新渲染!
這就是React.memo(...)這個函數(shù)牛X的地方!
在我們之前那個沒用到React.memo(...)的例子中,count的重復設置會使組件進行重新渲染??墒俏覀冇昧薘eact.memo后,該組件在傳入的值不變的前提下是不會被重新渲染的。
結(jié)論
以下是幾點總結(jié):
- React.PureComponent是銀
- React.memo(...)是金
- React.PureComponent是給ES6的類組件使用的
- React.memo(...)是給函數(shù)組件使用的
- React.PureComponent減少ES6的類組件的無用渲染
- React.memo(...)減少函數(shù)組件的無用渲染
- 為函數(shù)組件提供優(yōu)化是一個巨大的進步
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
react ant-design Select組件下拉框map不顯示的解決
這篇文章主要介紹了react ant-design Select組件下拉框map不顯示的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
高性能React開發(fā)React Server Components詳解
ReactServerComponents通過服務器端渲染、自動代碼分割等技術(shù),實現(xiàn)了高性能的React開發(fā),它解決了客戶端數(shù)據(jù)請求鏈式延遲、敏感數(shù)據(jù)暴露風險等問題,提供了更好的用戶體驗和安全性,本文介紹高性能React開發(fā)React Server Components詳解,感興趣的朋友一起看看吧2025-03-03
React實現(xiàn)基于Antd密碼強度校驗組件示例詳解
這篇文章主要為大家介紹了React實現(xiàn)基于Antd密碼強度校驗組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-01-01
解決React在安裝antd之后出現(xiàn)的Can''t resolve ''./locale''問題(推薦)
這篇文章主要介紹了解決React在安裝antd之后出現(xiàn)的Can't resolve './locale'問題,本文給大家分享解決方案,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05
解決React報錯useNavigate()?may?be?used?only?in?context?of
這篇文章主要為大家介紹了解決React報錯useNavigate()?may?be?used?only?in?context?of?Router,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12

