React前端渲染優(yōu)化--父組件導(dǎo)致子組件重復(fù)渲染的問題
React前端渲染優(yōu)化--父組件導(dǎo)致子組件重復(fù)渲染
說明
目前我們所使用 react 版本一般會有以下四種方式觸發(fā)渲染 render,而其中通過父組件 render 會直接通知子組件也進(jìn)行 render。

一般的優(yōu)化方式
鑒于此種情況,如果完全不做控制下,父組件 render, 那么子組件一定會 render。真實 dom 的渲染 react 會在 diff 算法之后合計出最小改動,進(jìn)行操作。但對于結(jié)構(gòu)復(fù)雜頁面,自頂向下,只是單純 diff 也要花費很長的時間來處理 js 任務(wù)。再加上我們每個組件的 render 中也會寫很多業(yè)務(wù)、數(shù)據(jù)處理。
js 為單線程執(zhí)行,顯然,不必要的子組件的 render 會浪費 js 線程資源,復(fù)雜任務(wù)還會長時間占用線程導(dǎo)致假死狀態(tài),也就是頁面卡頓,react 底層有 Fiber 來優(yōu)化任務(wù)隊列,但無法優(yōu)化業(yè)務(wù)代碼上的問題。
一般子組件可以通過確認(rèn) props 是否發(fā)生變化來控制自身是否進(jìn)行 render,比如 react-mobx 中的 observer 高階方法或者 React.PureComponet 就是用來做淺層比較進(jìn)行控制處理。
項目中常見會導(dǎo)致重復(fù)渲染的寫法以及改進(jìn)方法
函數(shù)導(dǎo)致的渲染重復(fù)
箭頭函數(shù) props.fn = () => {} 或者 綁定方法 props.fn = this.xxx.bind(this)
這樣的寫法每次父組件 render 都會新聲明一個 function 傳遞給子組件,會導(dǎo)致 observer 失去比對作用,父組件每次 render 都會使這個組件 render,嚴(yán)重影響性能!
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} // 【正確】
/>
)
}
}
字面量寫法導(dǎo)致的渲染重復(fù)
由于字面量的寫法{} 和 { pageSizeOptions: ['10'] },每次都會字面量聲明一個新的對象傳遞給列表組件,導(dǎo)致頁面重新 render。

toJS() 方法每次也會返回新對象,會導(dǎo)致頁面重新渲染
組件重復(fù)渲染問題(pureComponent, React.memo, useMemo, useCallback)
在一個組件中, 其state變化會引起render的重新執(zhí)行, 函數(shù)式組件中, 使用setHook更新state也會引起render的重新執(zhí)行
render執(zhí)行會帶來兩個方面的影響
- 1.當(dāng)前組件需要重新渲染, 除了那些狀態(tài)和生命周期初始化被保留的,其余正常的都會重新執(zhí)行。
- 2.子組件會重新渲染, 即使其是一個無狀態(tài)組件
針對上述問題, react給出來解決方案:
pureComponentReact.memouseMemouseCallback
下面將具體說明這幾個都使用場景和解決的問題
- useMemo設(shè)計的初衷就是避免重復(fù)進(jìn)行大規(guī)模的計算, 它的理想作用對象是當(dāng)前組件
具體是將當(dāng)前組件中一個經(jīng)過很復(fù)雜的計算得到的值緩存起來, 當(dāng)其依賴項不變的時候, 即使組件重新渲染, 也不會重新計算。
通過上述描述也能理解出其緩存的是一個具體的數(shù)據(jù)(可以和接下來的useCallback區(qū)分開)
/*
緩存了一個對象, 只有當(dāng)count變化時才會重新返回該對象
*/
const useInfo = useMemo(
() => ({
count: count,
name: "name"
}),
[count]
)
- 針對第二點, 分別有三個解決方案
首先是useCallback, 其語法和useMemo基本一致, 但是其使用場景是父組件定義了一個函數(shù)并且將這個函數(shù)傳遞給了子組件, 那么當(dāng)父組件重新渲染時,生成的會是一個新的函數(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ù)就是依賴,當(dāng)該依賴變化時,將會重新緩存該函數(shù)
其余useMemo的區(qū)別就在于,其緩存的是函數(shù)本身,而useMemo緩存的是函數(shù)計算后的值,都會在依賴項變化時重新緩存。
注:雖然其可能對于父組件傳遞給子組件函數(shù)時可能很理想,但實際上其帶來的性能損耗也是顯而易見的,其使用場景不應(yīng)該是擔(dān)心本組件的函數(shù)因為本組件重新渲染而重新生成,這樣反而起到了反效果,當(dāng)前組件更新,其重新渲染,內(nèi)部的函數(shù)也重新生成,其性能損耗可以忽略不計,如下圖。
使用場景應(yīng)該是父組件更新導(dǎo)致重新生成的函數(shù)又傳遞給了子組件,導(dǎo)致子組件重新渲染。

- 接著是pureComponent
它是一個類, 組件繼承自它后, 其作為子組件時, 每次父組件更新后, 會淺對比傳來的props是否變化, 若沒變化, 則子組件不更新。
- React.memo
同上條功能類似, 當(dāng)其作用于函數(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é),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
antd-react使用Select組件defaultValue踩的坑及解決
這篇文章主要介紹了antd-react使用Select組件defaultValue踩的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05
react結(jié)合bootstrap實現(xiàn)評論功能
這篇文章主要為大家詳細(xì)介紹了react結(jié)合bootstrap實現(xiàn)評論功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-05-05
React-View-UI組件庫封裝Loading加載中源碼
這篇文章主要介紹了React-View-UI組件庫封裝Loading加載樣式,主要包括組件介紹,組件源碼及組件測試源碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06
React-Route6實現(xiàn)keep-alive效果
本文主要介紹了React-Route6實現(xiàn)keep-alive效果,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧<BR>2022-06-06

