欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React.memo?React.useMemo對項目性能優(yōu)化使用詳解

 更新時間:2023年01月17日 10:15:16   作者:熬夜冠軍  
這篇文章主要為大家介紹了React.memo?React.useMemo對項目性能優(yōu)化的使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

React.memo

這篇文章會詳細(xì)介紹該何時、如何正確使用它,并且搭配 React.memo 來對我們的項目進(jìn)行一個性能優(yōu)化。

示例

我們先從一個簡單的示例入手

以下是一個常規(guī)的父子組件關(guān)系,打開瀏覽器控制臺并觀察,每次點擊父組件中的 + 號按鈕,都會導(dǎo)致子組件渲染。

const ReactNoMemoDemo = () => {
  const [count, setCount] = React.useState(0);
  return (
    <div>
      <div>Parent Count: {count}</div>
      <button onClick={() => setCount(count => count + 1)}>+</button>
      <Child name="Son" />
    </div>
  );
};
const Child = props => {
  console.log('子組件渲染了');
  return <p>Child Name: {props.name}</p>;
};
render(<ReactNoMemoDemo />);

子組件的 name 參數(shù)明明沒有被修改,為什么還是重新渲染?

這就是 React 的渲染機(jī)制,組件內(nèi)部的 state 或者 props 一旦發(fā)生修改,整個組件樹都會被重新渲染一次,即時子組件的參數(shù)沒有被修改,甚至無狀態(tài)組件。

如何處理這個問題?接下里就要說到 React.memo

介紹

React.memoReact 官方提供的一個高階組件,用于緩存我們的需要優(yōu)化的組件

如果你的組件在相同 props 的情況下渲染相同的結(jié)果,那么你可以通過將其包裝在 React.memo 中調(diào)用,以此通過記憶組件渲染結(jié)果的方式來提高組件的性能表現(xiàn)。這意味著在這種情況下,React 將跳過渲染組件的操作并直接復(fù)用最近一次渲染的結(jié)果。

讓我們來改進(jìn)一下上述的代碼,只需要使用 React.memo 組件包裹起來即可,其他用法不變

使用

function ReactMemoDemo() {
  const [count, setCount] = React.useState(0);
  return (
    <div>
      <div>Parent Count: {count}</div>
      <button onClick={() => setCount(count => count + 1)}>+</button>
      <Child name="Son" />
    </div>
  );
}
const Child = React.memo(props => {
  console.log('子組件渲染了');
  return <p>Child Name: {props.name}</p>;
});
render(<ReactMemoDemo />);

再次觀察控制臺,應(yīng)該會發(fā)現(xiàn)再點擊父組件的按鈕,子組件已經(jīng)不會重新渲染了。

這就是 React.memo 為我們做的緩存優(yōu)化,渲染 Child 組件之前,對比 props,發(fā)現(xiàn) name 沒有發(fā)生改變,因此返回了組件上一次的渲染的結(jié)果。

React.memo 僅檢查 props 變更。如果函數(shù)組件被 React.memo 包裹,且其實現(xiàn)中擁有 useState,useReducer 或 useContext 的 Hook,當(dāng) state 或 context 發(fā)生變化時,它仍會重新渲染。

當(dāng)然,如果我們子組件有內(nèi)部狀態(tài)并且發(fā)生了修改,依然會重新渲染(正常行為)。

FAQ

看到這里,不禁會產(chǎn)生疑問,既然如此,那我直接為每個組件都添加 React.memo 來進(jìn)行緩存就好了,再深究一下,為什么 React 不直接默認(rèn)為每個組件緩存呢?那這樣既節(jié)省了開發(fā)者的代碼,又為項目帶來了許多性能的優(yōu)化,這樣不好嗎?

使用太多的緩存,反而容易帶來 負(fù)提升。

前面有說到,組件使用緩存策略后,在被更新之前,會比較最新的 props 和上一次的 props 是否發(fā)生值修改,既然有比較,那就有計算,如果子組件的參數(shù)特別多且復(fù)雜繁重,那么這個比較的過程也會十分的消耗性能,甚至高于 虛擬 DOM 的生成,這時的緩存優(yōu)化,反而產(chǎn)生的負(fù)面影響,這個就是關(guān)鍵問題。

當(dāng)然,這種情況很少,大部分情況還是 組件樹的 虛擬 DOM 計算比緩存計算更消耗性能。但是,既然有這種極端問題發(fā)生,就應(yīng)該把選擇權(quán)交給開發(fā)者,讓我們自行決定是否需要對該組件進(jìn)行渲染,這也是 React 不默認(rèn)為組件設(shè)置緩存的原因。

也因此,在 React 社區(qū)中,開發(fā)者們也一致的認(rèn)為,不必要的情況下,不需要使用 React.memo

什么時候該用? 組件渲染過程特別消耗性能,以至于能感覺到到,比如:長列表、圖表等

什么時候不該用?組件參數(shù)結(jié)構(gòu)十分龐大復(fù)雜,比如未知層級的對象,或者列表(城市,用戶名)等

React.memo 二次優(yōu)化

React.memo 默認(rèn)每次會對復(fù)雜的對象做對比,如果你使用了 React.memo 緩存的組件參數(shù)十分復(fù)雜,且只有參數(shù)屬性內(nèi)的某些/某個字段會修改,或者根本不可能發(fā)生變化的情況下,你可以再粒度化的控制對比邏輯,通過 React.memo 第二個參數(shù)

function MyComponent(props) {
  /* 使用 props 渲染 */
}
function shouldMemo(prevProps, nextProps) {
  /*
  如果把 nextProps 傳入 render 方法的返回結(jié)果與
  將 prevProps 傳入 render 方法的返回結(jié)果一致則返回 true,
  否則返回 false
  */
}
export default React.memo(MyComponent, shouldMemo);

如果對 class 組件有了解過的朋友應(yīng)該知道,class 組件有一個生命周期叫做 shouldComponentUpdate(),也是通過對比 props 來告訴組件是否需要更新,但是與這個邏輯剛好相反。

小結(jié)

對于 React.memo,無需刻意去使用它進(jìn)行緩存組件,除非你能感覺到你需要。另外,不緩存的組件會多次的觸發(fā) render,因此,如果你在組件內(nèi)有打印信息,可能會被多次的觸發(fā),也不用去擔(dān)心,即使強(qiáng)制被 rerender,因為狀態(tài)沒有發(fā)生改變,因此每次 render 返回的值還是一樣,所以也不會觸發(fā)真實 dom 的更新,對頁面實際沒有任何影響。

useMemo

示例

同樣,我們先看一個例子,calculatedCount 變量是一個假造的比較消耗性能的計算表達(dá)式,為了方便顯示性能數(shù)據(jù)打印時間,我們使用了 IIFE 立即執(zhí)行函數(shù),每次計算 calculatedCount 都會輸出它的計算消耗時間。

打開控制臺,因為是 IIFE,所以首次會直接打印出時間。然后,再點擊 + 號,會發(fā)現(xiàn)再次打印出了計算耗時。這是因為 React 組件重渲染的時候,不僅是 jsx,而且變量,函數(shù)這種也全部都會再次聲明一次,因此導(dǎo)致了 calculatedCount 重新執(zhí)行了初始化(計算),但是這個變量值并沒有發(fā)生改變,如果每次渲染都要重新計算,那也是十分的消耗性能。

注意觀察,在計算期間,頁面會發(fā)生卡死,不能操作,這是 JS 引擎 的機(jī)制,在執(zhí)行任務(wù)的時候,頁面永遠(yuǎn)不會進(jìn)行渲染,直到任務(wù)結(jié)束為止。這個過程對用戶體驗來說是致命的,雖然我們可以通過微任務(wù)去處理這個計算過程,從而避免頁面的渲染阻塞,但是消耗性能這個問題仍然存在,我們需要通過其他方式去解決。

function UseMemoDemo() {
  const [count, setCount] = React.useState(0);
  const calculatedCount = (() => {
    let res = 0;
    const startTime = Date.now();
    for (let i = 0; i <= 100000000; i++) {
      res++;
    }
    console.log(`Calculated Count 計算耗時:${Date.now() - startTime} ms`);
    return res;
  })();
  return (
    <div>
      <div>Parent Count: {count}</div>
      <button onClick={() => setCount(count => count + 1)}>+</button>
      <div>Calculated Count: {calculatedCount}</div>
    </div>
  );
}

介紹

const memoizedValue = useMemo(() => {
  // 處理復(fù)雜計算,并 return 結(jié)果
}, []);

useMemo 返回一個緩存過的值,把 "創(chuàng)建" 函數(shù)和依賴項數(shù)組作為參數(shù)傳入 useMemo,它僅會在某個依賴項改變時才重新計算 memoized 值。這種優(yōu)化有助于避免在每次渲染時都進(jìn)行高開銷的計算

第一個參數(shù)是函數(shù),函數(shù)中需要返回計算值

第二個參數(shù)是依賴數(shù)組

  • 如果不傳,則每次都會初始化,緩存失敗
  • 如果傳空數(shù)組,則永遠(yuǎn)都會返回第一次執(zhí)行的結(jié)果
  • 如果傳狀態(tài),則在依賴的狀態(tài)變化時,才會從新計算,如果這個緩存狀態(tài)依賴了其他狀態(tài)的話,則需要提供進(jìn)去。

這下就很好理解了,我們的 calculatedCount 沒有任何外部依賴,因此只需要傳遞空數(shù)組作為第二個參數(shù),開始改造

使用

function UseMemoDemo() {
  const [count, setCount] = React.useState(0);
  const calculatedCount = useMemo(() => {
    let res = 0;
    const startTime = Date.now();
    for (let i = 0; i <= 100000000; i++) {
      res++;
    }
    console.log(`Memo Calculated Count 計算耗時:${Date.now() - startTime} ms`);
    return res;
  }, []);
  return (
    <div>
      <div>Parent Count: {count}</div>
      <button onClick={() => setCount(count => count + 1)}>+</button>
      <div>Memorized Calculated Count: {calculatedCount}</div>
    </div>
  );
}

現(xiàn)在,"Memo Calculated Count 計算耗時"的輸出信息永遠(yuǎn)只會打印一次,因為它被無限緩存了。

FAQ何時使用?

當(dāng)你的表達(dá)式十分復(fù)雜需要經(jīng)過大量計算的時候

示例

下面示例中,我們使用狀態(tài)提升,將子組件的 click 事件函數(shù)放在了父組件中,點擊父組件的 + 號,發(fā)現(xiàn)子組件被重新渲染

const FunctionPropDemo = () => {
  const [count, setCount] = React.useState(0);
  const handleChildClick = () => {
    //
  };
  return (
    <div>
      <div>Parent Count: {count}</div>
      <button onClick={() => setCount(count => count + 1)}>+</button>
      <Child onClick={handleChildClick} />
    </div>
  );
};
const Child = React.memo(props => {
  console.log('子組件渲染了');
  return (
    <div>
      <div>Child</div>
      <button onClick={props.onClick}>Click Me</button>
    </div>
  );
});
render(<FunctionPropDemo />);

于是我們想到用 memo 函數(shù)包裹子組件,給緩存起來

const FunctionPropDemo = () => {
  const [count, setCount] = React.useState(0);
  const handleChildClick = () => {
    //
  };
  return (
    <div>
      <div>Parent Count: {count}</div>
      <button onClick={() => setCount(count => count + 1)}>+</button>
      <Child onClick={handleChildClick} />
    </div>
  );
};
const Child = React.memo(props => {
  console.log('子組件渲染了');
  return (
    <div>
      <div>Child</div>
      <button onClick={props.onClick}>Click Me</button>
    </div>
  );
});
render(<FunctionPropDemo />);

但是意外來了,即使被 memo 包裹的組件,還是被重新渲染了,為什么!

我們來逐一分析

  • 首先,點擊父組件的 + 號,count 發(fā)生變化,于是父組件開始重渲染
  • 內(nèi)部的未經(jīng)處理的變量和函數(shù)都被重新初始化,useState 不會再初始化了, useEffect 鉤子函數(shù)重新執(zhí)行,虛擬 dom 更新
  • 執(zhí)行到 Child 組件的時候,Child 準(zhǔn)備更新,但是因為它是 memo 緩存組件,于是開始淺比較 props 參數(shù),到這里為止一切正常
  • Child 組件參數(shù)開始逐一比較變更,到了 onClick 函數(shù),發(fā)現(xiàn)值為函數(shù),提供的新值也為函數(shù),但是因為剛剛在父組件內(nèi)部重渲染時被重新初始化了(生成了新的地址),因為函數(shù)是引用類型值,導(dǎo)致引用地址發(fā)生改變!比較結(jié)果為不相等, React 仍會認(rèn)為它已更改,因此重新發(fā)生了渲染。

既然函數(shù)重新渲染會被重新初始化生成新的引用地址,因此我們應(yīng)該避免它重新初始化。這個時候,useMemo 的第二個使用場景就來了

const FunctionPropDemo = () => {
  const [count, setCount] = React.useState(0);
  const handleChildClick = useMemo(() => {
    return () => {
      //
    };
  }, []);
  return (
    <div>
      <div>Parent Count: {count}</div>
      <button onClick={() => setCount(count => count + 1)}>+</button>
      <Child onClick={handleChildClick} />
    </div>
  );
};
const Child = React.memo(props => {
  console.log('子組件渲染了');
  return (
    <div>
      <div>Child</div>
      <button onClick={props.onClick}>Click Me</button>
    </div>
  );
});
render(<FunctionPropDemo />);

這里我們將原本的 handleChildClick 函數(shù)通過 useMemo 包裹起來了,另外函數(shù)永遠(yuǎn)不會發(fā)生改變,因此傳遞第二參數(shù)為空數(shù)組,再次嘗試點擊 + 號,子組件不會被重新渲染了。

對于對象,數(shù)組,renderProps(參數(shù)為 react 組件) 等參數(shù),都可以使用 useMemo 進(jìn)行緩存

示例

既然 useMemo 可以緩存變量函數(shù)等,那組件其實也是一個函數(shù),能不能被緩存呢?我們試一試

繼續(xù)使用第一個案例,將 React.memo 移除,使用 useMemo 改造

const ReactNoMemoDemo = () => {
  const [count, setCount] = React.useState(0);
  const memorizedChild = useMemo(() => <Child name="Son" />, []);
  return (
    <div>
      <div>Parent Count: {count}</div>
      <button onClick={() => setCount(count => count + 1)}>+</button>
      {memorizedChild}
    </div>
  );
};
const Child = props => {
  console.log('子組件渲染了');
  return <p>Child Name: {props.name}</p>;
};
render(<ReactNoMemoDemo />);

嘗試點擊 + 號,是的,ChilduseMemo 緩存成功了!

小結(jié)

同樣的,不是必要的情況下,和 React.memo 一樣,不需要特別的使用 useMemo

使用場景

  • 表達(dá)式有復(fù)雜計算且不會頻發(fā)觸發(fā)更新
  • 引用類型的組件參數(shù),函數(shù),對象,數(shù)組等(一般情況下對象和數(shù)組都會從 useState 初始化,useState 不會二次執(zhí)行,主要是函數(shù)參數(shù))
  • react 組件的緩存

擴(kuò)展

useCallback

前面使用 useMemo 包裹了函數(shù),會感覺代碼結(jié)構(gòu)非常的奇怪

const handleChildClick = useMemo(() => {
  return () => {
    //
  };
}, []);

函數(shù)中又 return 了一個函數(shù),其實還有另一個推薦的 API, useCallback 來代替于對函數(shù)的緩存,兩者功能是完全一樣,只是使用方法的區(qū)別,useMemo 需要從第一個函數(shù)參數(shù)中 return 出要緩存的函數(shù),useCallback 則直接將函數(shù)傳入第一個參數(shù)即可

const handleChildClick = useCallback(() => {
  //
}, []);

代碼風(fēng)格上簡介明了了許多

看完這篇文章,相信你對 React.memoReact.useMemo 已經(jīng)有了一定的了解,并且知道何時/如何使用它們了

以上就是React.memo React.useMemo對項目性能優(yōu)化使用詳解的詳細(xì)內(nèi)容,更多關(guān)于React.memo React.useMemo性能優(yōu)化的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解React Angular Vue三大前端技術(shù)

    詳解React Angular Vue三大前端技術(shù)

    當(dāng)前世界中,技術(shù)發(fā)展非常迅速并且變化迅速,開發(fā)者需要更多的開發(fā)工具來解決不同的問題。本文就對于當(dāng)下主流的前端開發(fā)技術(shù)React、Vue、Angular這三個框架做個相對詳盡的探究,目的是為了解開這些前端技術(shù)的面紗,看看各自的廬山真面目。
    2021-05-05
  • React diff算法的實現(xiàn)示例

    React diff算法的實現(xiàn)示例

    這篇文章主要介紹了React diff算法的實現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • react native 文字輪播的實現(xiàn)示例

    react native 文字輪播的實現(xiàn)示例

    這篇文章主要介紹了react native 文字輪播的實現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • 詳解React 在服務(wù)端渲染的實現(xiàn)

    詳解React 在服務(wù)端渲染的實現(xiàn)

    這篇文章主要介紹了詳解React 在服務(wù)端渲染的實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • React.js綁定this的5種方法(小結(jié))

    React.js綁定this的5種方法(小結(jié))

    this在javascript中已經(jīng)相當(dāng)靈活,這篇文章主要介紹了React.js綁定this的5種方法(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • React使用api的方式封裝彈窗的示例代碼

    React使用api的方式封裝彈窗的示例代碼

    在現(xiàn)代開發(fā)中的彈窗樣式,經(jīng)常會是底部一個叉號樣式的彈窗,但是目前組件庫中并無類似彈窗組件,本文小編給大家介紹了React使用api的方式封裝彈窗的示例,感興趣的小伙伴跟著小編一起來看看吧
    2024-09-09
  • react源碼層探究setState作用

    react源碼層探究setState作用

    寫react的時候,踩了幾次坑發(fā)現(xiàn)setstate之后state不會立刻更新,于是判定setstate就是異步的方法,但是直到有一天,我想立刻拿到更新的state去傳參另一個方法的時候,才問自己,為什么setstate是異步的?準(zhǔn)確地說,在React內(nèi)部機(jī)制能檢測到的地方,setState就是異步的
    2022-10-10
  • React Native中導(dǎo)航組件react-navigation跨tab路由處理詳解

    React Native中導(dǎo)航組件react-navigation跨tab路由處理詳解

    這篇文章主要給大家介紹了關(guān)于React Native中導(dǎo)航組件react-navigation跨tab路由處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • 淺談React?Refs?使用場景及核心要點

    淺談React?Refs?使用場景及核心要點

    本文主要介紹了React?Refs?使用場景及核心要點,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • React自定義視頻全屏按鈕實現(xiàn)全屏功能

    React自定義視頻全屏按鈕實現(xiàn)全屏功能

    這篇文章主要介紹了React自定義視頻全屏按鈕實現(xiàn)全屏功能,通過繪制全屏按鈕,并綁定點擊事件,編寫點擊事件,通過實例代碼給大家詳細(xì)講解,需要的朋友可以參考下
    2022-11-11

最新評論