React前端渲染優(yōu)化--父組件導致子組件重復(fù)渲染的問題
React前端渲染優(yōu)化--父組件導致子組件重復(fù)渲染
說明
目前我們所使用 react 版本一般會有以下四種方式觸發(fā)渲染 render,而其中通過父組件 render 會直接通知子組件也進行 render。
一般的優(yōu)化方式
鑒于此種情況,如果完全不做控制下,父組件 render, 那么子組件一定會 render。真實 dom 的渲染 react 會在 diff 算法之后合計出最小改動,進行操作。但對于結(jié)構(gòu)復(fù)雜頁面,自頂向下,只是單純 diff 也要花費很長的時間來處理 js 任務(wù)。再加上我們每個組件的 render 中也會寫很多業(yè)務(wù)、數(shù)據(jù)處理。
js 為單線程執(zhí)行,顯然,不必要的子組件的 render 會浪費 js 線程資源,復(fù)雜任務(wù)還會長時間占用線程導致假死狀態(tài),也就是頁面卡頓,react 底層有 Fiber 來優(yōu)化任務(wù)隊列,但無法優(yōu)化業(yè)務(wù)代碼上的問題。
一般子組件可以通過確認 props 是否發(fā)生變化來控制自身是否進行 render,比如 react-mobx 中的 observer 高階方法或者 React.PureComponet 就是用來做淺層比較進行控制處理。
項目中常見會導致重復(fù)渲染的寫法以及改進方法
函數(shù)導致的渲染重復(fù)
箭頭函數(shù) props.fn = () => {} 或者 綁定方法 props.fn = this.xxx.bind(this)
這樣的寫法每次父組件 render 都會新聲明一個 function 傳遞給子組件,會導致 observer 失去比對作用,父組件每次 render 都會使這個組件 render,嚴重影響性能!
import React from 'react'; import { observer } from 'mobx-react'; // 我們開發(fā)中常見的一個被觀測組件,例如 ObserverComponent @observer class ObserverComponent extends React.Component { render() { return (<div>ObserverComponent</div>) } } // 例如在父組件 Parent 使用被觀測的子組件 ObserverComponent // 請不要給子組件 ObserverComponent 的 props 設(shè)置 箭頭函數(shù) () => {} 或者 fn.bind(this) 方法 @observer class Parent extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); // 【正確】 } handleChange() {} doSomething = () => {} render() { return ( <ObserverComponent onChange={() => {}} // 【錯誤】 onChange={this.handleChange.bind(this)} // 【錯誤】 onChange={this.handleChange} // 【正確】 todo={this.doSomething} // 【正確】 /> ) } }
字面量寫法導致的渲染重復(fù)
由于字面量的寫法{} 和 { pageSizeOptions: ['10'] },每次都會字面量聲明一個新的對象傳遞給列表組件,導致頁面重新 render。
toJS() 方法每次也會返回新對象,會導致頁面重新渲染
組件重復(fù)渲染問題(pureComponent, React.memo, useMemo, useCallback)
在一個組件中, 其state變化會引起render的重新執(zhí)行, 函數(shù)式組件中, 使用setHook更新state也會引起render的重新執(zhí)行
render執(zhí)行會帶來兩個方面的影響
- 1.當前組件需要重新渲染, 除了那些狀態(tài)和生命周期初始化被保留的,其余正常的都會重新執(zhí)行。
- 2.子組件會重新渲染, 即使其是一個無狀態(tài)組件
針對上述問題, react給出來解決方案:
pureComponent
React.memo
useMemo
useCallback
下面將具體說明這幾個都使用場景和解決的問題
- useMemo設(shè)計的初衷就是避免重復(fù)進行大規(guī)模的計算, 它的理想作用對象是當前組件
具體是將當前組件中一個經(jīng)過很復(fù)雜的計算得到的值緩存起來, 當其依賴項不變的時候, 即使組件重新渲染, 也不會重新計算。
通過上述描述也能理解出其緩存的是一個具體的數(shù)據(jù)(可以和接下來的useCallback區(qū)分開)
/* 緩存了一個對象, 只有當count變化時才會重新返回該對象 */ const useInfo = useMemo( () => ({ count: count, name: "name" }), [count] )
- 針對第二點, 分別有三個解決方案
首先是useCallback, 其語法和useMemo基本一致, 但是其使用場景是父組件定義了一個函數(shù)并且將這個函數(shù)傳遞給了子組件, 那么當父組件重新渲染時,生成的會是一個新的函數(shù), 這個時候就可以使用useCallback了,如下:
const Page = (props) => { const [count, setCount] = useState(0); const [name, setName] = useState('Child組件'); return ( <> <ChildMemo name={name} onClick={ useCallback((newName: string) => setName(newName), []) }/> {/* useCallback((newName: string) => setName(newName),[]) */} {/* 這里使用了useCallback優(yōu)化了傳遞給子組件的函數(shù),只初始化一次這個函數(shù),下次不產(chǎn)生新的函數(shù) </> ) }
上述是一個簡寫的形式,意思就是將傳遞給子組件的這個函數(shù)緩存了,其第二個參數(shù)就是依賴,當該依賴變化時,將會重新緩存該函數(shù)
其余useMemo的區(qū)別就在于,其緩存的是函數(shù)本身,而useMemo緩存的是函數(shù)計算后的值,都會在依賴項變化時重新緩存。
注:雖然其可能對于父組件傳遞給子組件函數(shù)時可能很理想,但實際上其帶來的性能損耗也是顯而易見的,其使用場景不應(yīng)該是擔心本組件的函數(shù)因為本組件重新渲染而重新生成,這樣反而起到了反效果,當前組件更新,其重新渲染,內(nèi)部的函數(shù)也重新生成,其性能損耗可以忽略不計,如下圖。
使用場景應(yīng)該是父組件更新導致重新生成的函數(shù)又傳遞給了子組件,導致子組件重新渲染。
- 接著是pureComponent
它是一個類, 組件繼承自它后, 其作為子組件時, 每次父組件更新后, 會淺對比傳來的props是否變化, 若沒變化, 則子組件不更新。
- React.memo
同上條功能類似, 當其作用于函數(shù)式組件并且作為子組件時, 每次父組件更新后, 會淺對比傳來的props是否變化, 若沒變化, 則子組件不更新。
// 子組件暴露時暴露為處理后的組件 import {memo} from 'react' const TeacherModal = (props: any) => { return <div></div> } export default memo(TeacherModal)
上面兩個都區(qū)別在于, 一個是類, 一個是高階組件, 前者作用于類后者作用于函數(shù)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
react native 原生模塊橋接的簡單說明小結(jié)
這篇文章主要介紹了react native 原生模塊橋接的簡單說明小結(jié),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-02-02antd-react使用Select組件defaultValue踩的坑及解決
這篇文章主要介紹了antd-react使用Select組件defaultValue踩的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05react結(jié)合bootstrap實現(xiàn)評論功能
這篇文章主要為大家詳細介紹了react結(jié)合bootstrap實現(xiàn)評論功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-05-05React-View-UI組件庫封裝Loading加載中源碼
這篇文章主要介紹了React-View-UI組件庫封裝Loading加載樣式,主要包括組件介紹,組件源碼及組件測試源碼,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06React-Route6實現(xiàn)keep-alive效果
本文主要介紹了React-Route6實現(xiàn)keep-alive效果,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧<BR>2022-06-06