摘要:函數(shù)組件上面我們探討了如何使用和的方法優(yōu)化類(lèi)組件的性能。它的作用和類(lèi)似,是用來(lái)控制函數(shù)組件的重新渲染的。其實(shí)就是函數(shù)組件的。
原文鏈接: Improving Performance in React Functional Component using React.memo
原文作者: Chidume Nnamdi
譯者: 進(jìn)擊的大蔥
推薦理由: 本文講述了開(kāi)發(fā)React應(yīng)用時(shí)如何使用shouldComponentUpdate生命周期函數(shù)以及PureComponent去避免類(lèi)組件進(jìn)行無(wú)用的重渲染,以及如何使用最新的React.memo API去優(yōu)化函數(shù)組件的性能。
React核心開(kāi)發(fā)團(tuán)隊(duì)一直都努力地讓React變得更快。在React中可以用來(lái)優(yōu)化組件性能的方法大概有以下幾種:
組件懶加載(React.lazy(...)和
Pure Component
shouldComponentUpdate(...){...}生命周期函數(shù)
本文還會(huì)介紹React16.6加入的另外一個(gè)專(zhuān)門(mén)用來(lái)優(yōu)化函數(shù)組件(Functional Component)性能的方法: React.memo。
無(wú)用的渲染組件是構(gòu)成React視圖的一個(gè)基本單元。有些組件會(huì)有自己本地的狀態(tài)(state), 當(dāng)它們的值由于用戶的操作而發(fā)生改變時(shí),組件就會(huì)重新渲染。在一個(gè)React應(yīng)用中,一個(gè)組件可能會(huì)被頻繁地進(jìn)行渲染。這些渲染雖然有一小部分是必須的,不過(guò)大多數(shù)都是無(wú)用的,它們的存在會(huì)大大降低我們應(yīng)用的性能。
看下面這個(gè)例子:
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 ({this.state.count}); } } export default TestC;
TestC組件有一個(gè)本地狀態(tài)count,它的初始值是0(state = {count: 0})。當(dāng)我們點(diǎn)擊Click Me按鈕時(shí),count的值被設(shè)置為1。這時(shí)候屏幕的數(shù)字將會(huì)由0變成1。當(dāng)我們?cè)俅吸c(diǎn)擊該按鈕時(shí),count的值還是1, 這時(shí)候TestC組件不應(yīng)該被重新渲染,可是現(xiàn)實(shí)是這樣的嗎?
為了測(cè)試count重復(fù)設(shè)置相同的值組件會(huì)不會(huì)被重新渲染, 我為T(mén)estC組件添加了兩個(gè)生命周期函數(shù): componentWillUpdate和componentDidUpdate。componentWillUpdate方法在組件將要被重新渲染時(shí)被調(diào)用,而componentDidUpdate方法會(huì)在組件成功重渲染后被調(diào)用。
在瀏覽器中運(yùn)行我們的代碼,然后多次點(diǎn)擊Click Me按鈕,你可以看到以下輸出:
我們可以看到"componentWillUpdate"和"componentWillUpdate"在每次我們點(diǎn)擊完按鈕后,都會(huì)在控制臺(tái)輸出來(lái)。所以即使count被設(shè)置相同的值,TestC組件還是會(huì)被重新渲染,這些就是所謂的無(wú)用渲染。
為了避免React組件的無(wú)用渲染,我們可以實(shí)現(xiàn)自己的shouldComponentUpdate生命周期函數(shù)。
當(dāng)React想要渲染一個(gè)組件的時(shí)候,它將會(huì)調(diào)用這個(gè)組件的shouldComponentUpdate函數(shù), 這個(gè)函數(shù)會(huì)告訴它是不是真的要渲染這個(gè)組件。
如果我們的shouldComponentUpdate函數(shù)這樣寫(xiě):
shouldComponentUpdate(nextProps, nextState) { return true }
其中各個(gè)參數(shù)的含義是:
nextProps: 組件將會(huì)接收的下一個(gè)參數(shù)props
nextProps: 組件的下一個(gè)狀態(tài)state
因?yàn)槲覀兊膕houldComponentUpdate函數(shù)一直返回true,這就告訴React,無(wú)論何種情況都要重新渲染該組件。
可是如果我們這么寫(xiě):
shouldComponentUpdate(nextProps, nextState) { return false }
因?yàn)檫@個(gè)方法的返回值是false,所以React永遠(yuǎn)都不會(huì)重新渲染我們的組件。
因此當(dāng)你想要React重新渲染你的組件的時(shí)候,就在這個(gè)方法中返回true,否則返回false?,F(xiàn)在讓我們用shouldComponentUpdate重寫(xiě)之前的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 ({ this.state.count }); } } export default TestC;
我們?cè)赥estC組件里添加了shouldComponentUpdate方法,判斷如果現(xiàn)在狀態(tài)的count和下一個(gè)狀態(tài)的count一樣時(shí),我們返回false,這樣React將不會(huì)進(jìn)行組件的重新渲染,反之,如果它們兩個(gè)的值不一樣,就返回true,這樣組件將會(huì)重新進(jìn)行渲染。
再次在瀏覽器中測(cè)試我們的組件,剛開(kāi)始的界面是這樣的:
這時(shí)候,就算我們多次點(diǎn)擊Click Me按鈕,也只能看到兩行輸出:
componentWillUpdate componentDidUpdate
因?yàn)榈诙吸c(diǎn)擊Click Me按鈕后count值一直是1,這樣shouldComponentUpdate一直返回false,所以組件就不再被重新渲染了。
那么如何驗(yàn)證后面state的值發(fā)生改變,組件還是會(huì)被重新渲染呢?我們可以在瀏覽器的React DevTools插件中直接對(duì)TestC組件的狀態(tài)進(jìn)行更改。具體做法是, 在Chrome調(diào)試工具中點(diǎn)擊React標(biāo)簽,在界面左邊選中TestC組件,在界面的右邊就可以看到其狀態(tài)state中只有一個(gè)鍵count,且其值是1:
然后讓我們點(diǎn)擊count的值1,將其修改為2,然后按回車(chē)鍵:
你將會(huì)看到控制臺(tái)有以下輸出:
componentWillUpdate componentDidUpdate componentWillUpdate componentDidUpdate
state的count被改變了,組件也被重新渲染了。
現(xiàn)在讓我們使用另外一種方法PureComponent來(lái)對(duì)組件進(jìn)行優(yōu)化。
React在v15.5的時(shí)候引入了Pure Component組件。React在進(jìn)行組件更新時(shí),如果發(fā)現(xiàn)這個(gè)組件是一個(gè)PureComponent,它會(huì)將組件現(xiàn)在的state和props和其下一個(gè)state和props進(jìn)行淺比較,如果它們的值沒(méi)有變化,就不會(huì)進(jìn)行更新。要想讓你的組件成為Pure Component,只需要extends React.PureComponent即可。
讓我們用PureComponent去改寫(xiě)一下我們的代碼吧:
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 ({ this.state.count }); } } export default TestC;
在上面的代碼中,我將shouldComponentUpdate的代碼注釋掉了,因?yàn)镽eact.PureComponent本身就幫我們實(shí)現(xiàn)了一樣的功能。
改完代碼后,我們刷新一下瀏覽器,然后多次點(diǎn)擊Click Me按鈕看組件被渲染了多少遍:
由上面的輸出可知,我們的component只在state由0變?yōu)?時(shí)被重新渲染了,后面都沒(méi)有進(jìn)行渲染。
上面我們探討了如何使用PureComponent和shouldComponentUpdate的方法優(yōu)化類(lèi)組件的性能。雖然類(lèi)組件是React應(yīng)用的主要組成部分,不過(guò)函數(shù)組件(Functional Component)同樣可以被作為React組件使用。
function TestC(props) { return (I am a functional component) }
對(duì)于函數(shù)組件,它們沒(méi)有諸如state的東西去保存它們本地的狀態(tài)(雖然在React Hooks中函數(shù)組件可以使用useState去使用狀態(tài)), 所以我們不能像在類(lèi)組件中使用shouldComponentUpdate等生命函數(shù)去控制函數(shù)組件的重渲染。當(dāng)然,我們也不能使用extends React.PureComponent了,因?yàn)樗鼔焊筒皇且粋€(gè)類(lèi)。
要探討解決方案,讓我們先驗(yàn)證一下函數(shù)組件是不是也有和類(lèi)組件一樣的無(wú)用渲染的問(wèn)題。
首先我們先將ES6的TestC類(lèi)轉(zhuǎn)換為一個(gè)函數(shù)組件:
import React from "react"; const TestC = (props) => { console.log(`Rendering TestC :` props) return ({props.count}) } export default TestC; // App.js
當(dāng)上面的代碼初次加載時(shí),控制臺(tái)的輸出是:
同樣,我們可以打開(kāi)Chrome的調(diào)試工具,點(diǎn)擊React標(biāo)簽然后選中TestC組件:
我們可以看到這個(gè)組件的參數(shù)值是5,讓我們將這個(gè)值改為45, 這時(shí)候?yàn)g覽器輸出:
由于count的值改變了,所以該組件也被重新渲染了,控制臺(tái)輸出Object{count: 45},讓我們重復(fù)設(shè)置count的值為45, 然后再看一下控制臺(tái)的輸出結(jié)果:
由輸出結(jié)果可以看出,即使count的值保持不變,還是45, 該組件還是被重渲染了。
既然函數(shù)組件也有無(wú)用渲染的問(wèn)題,我們?nèi)绾螌?duì)其進(jìn)行優(yōu)化呢?
解決方案: 使用React.memo()React.memo(...)是React v16.6引進(jìn)來(lái)的新屬性。它的作用和React.PureComponent類(lèi)似,是用來(lái)控制函數(shù)組件的重新渲染的。React.memo(...) 其實(shí)就是函數(shù)組件的React.PureComponent。
如何使用React.memo(...)?React.memo使用起來(lái)非常簡(jiǎn)單,假設(shè)你有以下的函數(shù)組件:
const Funcomponent = ()=> { return (Hiya!! I am a Funtional component) }
我們只需將上面的Funcomponent作為參數(shù)傳入React.memo中:
const Funcomponent = ()=> { return (Hiya!! I am a Funtional component) } const MemodFuncComponent = React.memo(FunComponent)
React.memo會(huì)返回一個(gè)純化(purified)的組件MemoFuncComponent,這個(gè)組件將會(huì)在JSX標(biāo)記中渲染出來(lái)。當(dāng)組件的參數(shù)props和狀態(tài)state發(fā)生改變時(shí),React將會(huì)檢查前一個(gè)狀態(tài)和參數(shù)是否和下一個(gè)狀態(tài)和參數(shù)是否相同,如果相同,組件將不會(huì)被渲染,如果不同,組件將會(huì)被重新渲染。
現(xiàn)在讓我們?cè)赥estC組件上使用React.memo進(jìn)行優(yōu)化:
let TestC = (props) => { console.log("Rendering TestC :", props) return ({ props.count } > ) } TestC = React.memo(TestC);打開(kāi)瀏覽器重新加載我們的應(yīng)用。然后打開(kāi)Chrome調(diào)試工具,點(diǎn)擊React標(biāo)簽,然后選中
組件。 接著編輯一下props的值,將count改為89,我們將會(huì)看到我們的應(yīng)用被重新渲染了:
然后重復(fù)設(shè)置count的值為89:
這里沒(méi)有重新渲染!這就是React.memo(...)這個(gè)函數(shù)牛X的地方!
在我們之前那個(gè)沒(méi)用到React.memo(...)的例子中,count的重復(fù)設(shè)置會(huì)使組件進(jìn)行重新渲染??墒俏覀冇昧薘eact.memo后,該組件在傳入的值不變的前提下是不會(huì)被重新渲染的。
結(jié)論以下是幾點(diǎn)總結(jié):
React.PureComponent是銀
React.memo(...)是金
React.PureComponent是給ES6的類(lèi)組件使用的
React.memo(...)是給函數(shù)組件使用的
React.PureComponent減少ES6的類(lèi)組件的無(wú)用渲染
React.memo(...)減少函數(shù)組件的無(wú)用渲染
為函數(shù)組件提供優(yōu)化是一個(gè)巨大的進(jìn)步
我是進(jìn)擊的大蔥,關(guān)注我的公眾號(hào),獲取我分享的最新技術(shù)推送!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/102669.html
摘要:比如就是一種,它可以用來(lái)管理狀態(tài)返回的結(jié)果是數(shù)組,數(shù)組的第一項(xiàng)是值,第二項(xiàng)是賦值函數(shù),函數(shù)的第一個(gè)參數(shù)就是默認(rèn)值,也支持回調(diào)函數(shù)。而之所以輸出還是正確的,原因是的回調(diào)函數(shù)中,值永遠(yuǎn)指向最新的值,因此沒(méi)有邏輯漏洞。 1. 引言 如果你在使用 React 16,可以嘗試 Function Component 風(fēng)格,享受更大的靈活性。但在嘗試之前,最好先閱讀本文,對(duì) Function Com...
想要升職加薪就要努力學(xué)習(xí)這篇React不能將useMemo設(shè)置為默認(rèn)方法原因詳解, 很多朋友都建議可以用 React 這樣就不直接默認(rèn)使用這種memorized呢?還可以讓所有資料都緩存~減少渲染 話不多說(shuō),直接上。大概就是直接讓所有的東西都 默認(rèn)套上一層useMemo (or 其他的xxx)不就好了? 還真不行~ 你能學(xué)到 / 本文框架 memo constMyComponent...
摘要:的返回值將作為的參數(shù),如果返回,則不更新,不能返回或以外的值,否則會(huì)警告。在更新之前調(diào)用,此時(shí)已更新返回值作為的第個(gè)參數(shù)一般用于獲取之前的數(shù)據(jù)語(yǔ)法是從的返回值,默認(rèn)是的使用場(chǎng)景一般是獲取組建更新之前的滾動(dòng)條位置。 React16 后的各功能點(diǎn)是多個(gè)版本陸陸續(xù)續(xù)迭代增加的,本篇文章的講解是建立在 16.6.0 版本上本篇文章主旨在介紹 React16 之后版本中新增或修改的地方,所以對(duì)于...
摘要:接收一個(gè)屬性,這個(gè)組件會(huì)讓后代組件統(tǒng)一提供這個(gè)變量值。因此對(duì)于同一個(gè)對(duì)象而言,一定是后代元素。解決方法就是把內(nèi)聯(lián)函數(shù)提取出來(lái),如下講了這么多,我們還沒(méi)有講到其實(shí)我們已經(jīng)講完了的工作原理了。 本節(jié)主要講解以下幾個(gè)新的特性: Context ContextType lazy Suspense 錯(cuò)誤邊界(Error boundaries) memo 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博...
摘要:即使中沒(méi)有錯(cuò)誤,仍然會(huì)執(zhí)行,這一點(diǎn)一般都是知道的。我們認(rèn)為這是正確的前進(jìn)道路,兼具戰(zhàn)略性和務(wù)實(shí)性降低使用門(mén)檻開(kāi)發(fā)人員遷移到的障礙之一是從到的并不輕松的遷移。下一步將通過(guò)一系列功能和插件為的平滑過(guò)渡提供支持,并以此回饋社區(qū)。 showImg(https://segmentfault.com/img/remote/1460000017516912?w=1200&h=630); useSt...
閱讀 565·2023-04-26 01:39
閱讀 4590·2021-11-16 11:45
閱讀 2646·2021-09-27 13:37
閱讀 925·2021-09-01 10:50
閱讀 3649·2021-08-16 10:50
閱讀 2251·2019-08-30 15:55
閱讀 3024·2019-08-30 15:55
閱讀 2286·2019-08-30 14:07