摘要:函數組件上面我們探討了如何使用和的方法優化類組件的性能。它的作用和類似,是用來控制函數組件的重新渲染的。其實就是函數組件的。
原文鏈接: Improving Performance in React Functional Component using React.memo
原文作者: Chidume Nnamdi
譯者: 進擊的大蔥
推薦理由: 本文講述了開發React應用時如何使用shouldComponentUpdate生命周期函數以及PureComponent去避免類組件進行無用的重渲染,以及如何使用最新的React.memo API去優化函數組件的性能。
React核心開發團隊一直都努力地讓React變得更快。在React中可以用來優化組件性能的方法大概有以下幾種:
組件懶加載(React.lazy(...)和
Pure Component
shouldComponentUpdate(...){...}生命周期函數
本文還會介紹React16.6加入的另外一個專門用來優化函數組件(Functional Component)性能的方法: React.memo。
無用的渲染組件是構成React視圖的一個基本單元。有些組件會有自己本地的狀態(state), 當它們的值由于用戶的操作而發生改變時,組件就會重新渲染。在一個React應用中,一個組件可能會被頻繁地進行渲染。這些渲染雖然有一小部分是必須的,不過大多數都是無用的,它們的存在會大大降低我們應用的性能。
看下面這個例子:
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組件有一個本地狀態count,它的初始值是0(state = {count: 0})。當我們點擊Click Me按鈕時,count的值被設置為1。這時候屏幕的數字將會由0變成1。當我們再次點擊該按鈕時,count的值還是1, 這時候TestC組件不應該被重新渲染,可是現實是這樣的嗎?
為了測試count重復設置相同的值組件會不會被重新渲染, 我為TestC組件添加了兩個生命周期函數: componentWillUpdate和componentDidUpdate。componentWillUpdate方法在組件將要被重新渲染時被調用,而componentDidUpdate方法會在組件成功重渲染后被調用。
在瀏覽器中運行我們的代碼,然后多次點擊Click Me按鈕,你可以看到以下輸出:
我們可以看到"componentWillUpdate"和"componentWillUpdate"在每次我們點擊完按鈕后,都會在控制臺輸出來。所以即使count被設置相同的值,TestC組件還是會被重新渲染,這些就是所謂的無用渲染。
為了避免React組件的無用渲染,我們可以實現自己的shouldComponentUpdate生命周期函數。
當React想要渲染一個組件的時候,它將會調用這個組件的shouldComponentUpdate函數, 這個函數會告訴它是不是真的要渲染這個組件。
如果我們的shouldComponentUpdate函數這樣寫:
shouldComponentUpdate(nextProps, nextState) { return true }
其中各個參數的含義是:
nextProps: 組件將會接收的下一個參數props
nextProps: 組件的下一個狀態state
因為我們的shouldComponentUpdate函數一直返回true,這就告訴React,無論何種情況都要重新渲染該組件。
可是如果我們這么寫:
shouldComponentUpdate(nextProps, nextState) { return false }
因為這個方法的返回值是false,所以React永遠都不會重新渲染我們的組件。
因此當你想要React重新渲染你的組件的時候,就在這個方法中返回true,否則返回false。現在讓我們用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 ({ this.state.count }); } } export default TestC;
我們在TestC組件里添加了shouldComponentUpdate方法,判斷如果現在狀態的count和下一個狀態的count一樣時,我們返回false,這樣React將不會進行組件的重新渲染,反之,如果它們兩個的值不一樣,就返回true,這樣組件將會重新進行渲染。
再次在瀏覽器中測試我們的組件,剛開始的界面是這樣的:
這時候,就算我們多次點擊Click Me按鈕,也只能看到兩行輸出:
componentWillUpdate componentDidUpdate
因為第二次點擊Click Me按鈕后count值一直是1,這樣shouldComponentUpdate一直返回false,所以組件就不再被重新渲染了。
那么如何驗證后面state的值發生改變,組件還是會被重新渲染呢?我們可以在瀏覽器的React DevTools插件中直接對TestC組件的狀態進行更改。具體做法是, 在Chrome調試工具中點擊React標簽,在界面左邊選中TestC組件,在界面的右邊就可以看到其狀態state中只有一個鍵count,且其值是1:
然后讓我們點擊count的值1,將其修改為2,然后按回車鍵:
你將會看到控制臺有以下輸出:
componentWillUpdate componentDidUpdate componentWillUpdate componentDidUpdate
state的count被改變了,組件也被重新渲染了。
現在讓我們使用另外一種方法PureComponent來對組件進行優化。
React在v15.5的時候引入了Pure Component組件。React在進行組件更新時,如果發現這個組件是一個PureComponent,它會將組件現在的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 ({ this.state.count }); } } export default TestC;
在上面的代碼中,我將shouldComponentUpdate的代碼注釋掉了,因為React.PureComponent本身就幫我們實現了一樣的功能。
改完代碼后,我們刷新一下瀏覽器,然后多次點擊Click Me按鈕看組件被渲染了多少遍:
由上面的輸出可知,我們的component只在state由0變為1時被重新渲染了,后面都沒有進行渲染。
上面我們探討了如何使用PureComponent和shouldComponentUpdate的方法優化類組件的性能。雖然類組件是React應用的主要組成部分,不過函數組件(Functional Component)同樣可以被作為React組件使用。
function TestC(props) { return (I am a functional component) }
對于函數組件,它們沒有諸如state的東西去保存它們本地的狀態(雖然在React Hooks中函數組件可以使用useState去使用狀態), 所以我們不能像在類組件中使用shouldComponentUpdate等生命函數去控制函數組件的重渲染。當然,我們也不能使用extends React.PureComponent了,因為它壓根就不是一個類。
要探討解決方案,讓我們先驗證一下函數組件是不是也有和類組件一樣的無用渲染的問題。
首先我們先將ES6的TestC類轉換為一個函數組件:
import React from "react"; const TestC = (props) => { console.log(`Rendering TestC :` props) return ({props.count}) } export default TestC; // App.js
當上面的代碼初次加載時,控制臺的輸出是:
同樣,我們可以打開Chrome的調試工具,點擊React標簽然后選中TestC組件:
我們可以看到這個組件的參數值是5,讓我們將這個值改為45, 這時候瀏覽器輸出:
由于count的值改變了,所以該組件也被重新渲染了,控制臺輸出Object{count: 45},讓我們重復設置count的值為45, 然后再看一下控制臺的輸出結果:
由輸出結果可以看出,即使count的值保持不變,還是45, 該組件還是被重渲染了。
既然函數組件也有無用渲染的問題,我們如何對其進行優化呢?
解決方案: 使用React.memo()React.memo(...)是React v16.6引進來的新屬性。它的作用和React.PureComponent類似,是用來控制函數組件的重新渲染的。React.memo(...) 其實就是函數組件的React.PureComponent。
如何使用React.memo(...)?React.memo使用起來非常簡單,假設你有以下的函數組件:
const Funcomponent = ()=> { return (Hiya!! I am a Funtional component) }
我們只需將上面的Funcomponent作為參數傳入React.memo中:
const Funcomponent = ()=> { return (Hiya!! I am a Funtional component) } const MemodFuncComponent = React.memo(FunComponent)
React.memo會返回一個純化(purified)的組件MemoFuncComponent,這個組件將會在JSX標記中渲染出來。當組件的參數props和狀態state發生改變時,React將會檢查前一個狀態和參數是否和下一個狀態和參數是否相同,如果相同,組件將不會被渲染,如果不同,組件將會被重新渲染。
現在讓我們在TestC組件上使用React.memo進行優化:
let TestC = (props) => { console.log("Rendering TestC :", props) return ({ props.count } > ) } TestC = React.memo(TestC);打開瀏覽器重新加載我們的應用。然后打開Chrome調試工具,點擊React標簽,然后選中
組件。 接著編輯一下props的值,將count改為89,我們將會看到我們的應用被重新渲染了:
然后重復設置count的值為89:
這里沒有重新渲染!這就是React.memo(...)這個函數牛X的地方!
在我們之前那個沒用到React.memo(...)的例子中,count的重復設置會使組件進行重新渲染。可是我們用了React.memo后,該組件在傳入的值不變的前提下是不會被重新渲染的。
結論以下是幾點總結:
React.PureComponent是銀
React.memo(...)是金
React.PureComponent是給ES6的類組件使用的
React.memo(...)是給函數組件使用的
React.PureComponent減少ES6的類組件的無用渲染
React.memo(...)減少函數組件的無用渲染
為函數組件提供優化是一個巨大的進步
我是進擊的大蔥,關注我的公眾號,獲取我分享的最新技術推送!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102669.html
摘要:比如就是一種,它可以用來管理狀態返回的結果是數組,數組的第一項是值,第二項是賦值函數,函數的第一個參數就是默認值,也支持回調函數。而之所以輸出還是正確的,原因是的回調函數中,值永遠指向最新的值,因此沒有邏輯漏洞。 1. 引言 如果你在使用 React 16,可以嘗試 Function Component 風格,享受更大的靈活性。但在嘗試之前,最好先閱讀本文,對 Function Com...
想要升職加薪就要努力學習這篇React不能將useMemo設置為默認方法原因詳解, 很多朋友都建議可以用 React 這樣就不直接默認使用這種memorized呢?還可以讓所有資料都緩存~減少渲染 話不多說,直接上。大概就是直接讓所有的東西都 默認套上一層useMemo (or 其他的xxx)不就好了? 還真不行~ 你能學到 / 本文框架 memo constMyComponent...
摘要:的返回值將作為的參數,如果返回,則不更新,不能返回或以外的值,否則會警告。在更新之前調用,此時已更新返回值作為的第個參數一般用于獲取之前的數據語法是從的返回值,默認是的使用場景一般是獲取組建更新之前的滾動條位置。 React16 后的各功能點是多個版本陸陸續續迭代增加的,本篇文章的講解是建立在 16.6.0 版本上本篇文章主旨在介紹 React16 之后版本中新增或修改的地方,所以對于...
摘要:接收一個屬性,這個組件會讓后代組件統一提供這個變量值。因此對于同一個對象而言,一定是后代元素。解決方法就是把內聯函數提取出來,如下講了這么多,我們還沒有講到其實我們已經講完了的工作原理了。 本節主要講解以下幾個新的特性: Context ContextType lazy Suspense 錯誤邊界(Error boundaries) memo 想閱讀更多優質文章請猛戳GitHub博...
閱讀 540·2023-04-26 01:39
閱讀 4503·2021-11-16 11:45
閱讀 2616·2021-09-27 13:37
閱讀 886·2021-09-01 10:50
閱讀 3594·2021-08-16 10:50
閱讀 2222·2019-08-30 15:55
閱讀 2986·2019-08-30 15:55
閱讀 2262·2019-08-30 14:07