最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入React?18源碼useMemo?useCallback?memo用法及區(qū)別分析

 更新時(shí)間:2023年04月23日 08:39:22   作者:南山種子外賣跑手  
這篇文章主要為大家介紹了React?18源碼深入分析useMemo?useCallback?memo用法及區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

開篇

哈嘍大咖好,我是跑手,最近在做 React 相關(guān)的組件搭建,因?yàn)樯婕暗酱罅康膱D形計(jì)算以及頁面渲染,所以特意翻了下性能優(yōu)化相關(guān)的hooks使用,如 useMemo、useCallbackmemo。在這篇文章中,我們將探討這些功能的用法和區(qū)別,并通過源碼分析來理解它們的工作原理,開整!

用法

useMemo

useMemo 是一個(gè)用于優(yōu)化性能的 React 鉤子。它可以幫助我們避免在組件重新渲染時(shí)執(zhí)行昂貴的計(jì)算。useMemo 接受兩個(gè)參數(shù):一個(gè)函數(shù)和一個(gè)依賴數(shù)組。當(dāng)依賴數(shù)組中的值發(fā)生變化時(shí),useMemo 會(huì)重新計(jì)算并返回新的值。否則,它將返回上一次計(jì)算的值。

一個(gè)簡(jiǎn)單的例子:

import React, { useMemo } from "react";
function ExpensiveComponent({ a, b }) {
  const result = useMemo(() => {
    console.log("Expensive calculation...");
    return a * b;
  }, [a, b]);
  return <div>Result: {result}</div>;
}

我們創(chuàng)建了一個(gè)名為 ExpensiveComponent 的組件,它接受兩個(gè)屬性 ab 并使用 useMemo 鉤子來計(jì)算 ab 的乘積。當(dāng) ab 發(fā)生變化時(shí),useMemo 會(huì)重新計(jì)算結(jié)果。否則,它將返回上一次計(jì)算的值,避免了不必要的計(jì)算。

useCallback

useCallback 是另一個(gè)用于優(yōu)化性能的 React 鉤子。它可以幫助我們避免在組件重新渲染時(shí)創(chuàng)建新的函數(shù)實(shí)例。useCallback 接受兩個(gè)參數(shù):一個(gè)函數(shù)和一個(gè)依賴數(shù)組。當(dāng)依賴數(shù)組中的值發(fā)生變化時(shí),useCallback 會(huì)返回一個(gè)新的函數(shù)實(shí)例。否則,它將返回上一次創(chuàng)建的函數(shù)實(shí)例。

再看一個(gè)簡(jiǎn)單的例子:

import React, { useCallback } from "react";
function ButtonComponent({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}
function ParentComponent() {
  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []);
  return (
    <div>
      <ButtonComponent onClick={handleClick}>Click me</ButtonComponent>
    </div>
  );
}

在這個(gè)例子中,我們創(chuàng)建了一個(gè)名為 ButtonComponent 的組件,它接受一個(gè) onClick 函數(shù)屬性。我們還創(chuàng)建了一個(gè)名為 ParentComponent 的組件,它使用 useCallback 鉤子來創(chuàng)建一個(gè) handleClick 函數(shù)。當(dāng) ParentComponent 重新渲染時(shí),useCallback 會(huì)返回上一次創(chuàng)建的 handleClick 函數(shù)實(shí)例,避免了不必要的函數(shù)創(chuàng)建。

memo

memo 是一個(gè)用于優(yōu)化性能的 React 高階組件。它可以幫助我們避免在父組件重新渲染時(shí)重新渲染子組件。memo 接受一個(gè)組件作為參數(shù),并返回一個(gè)新的組件。當(dāng)新組件的屬性發(fā)生變化時(shí),它會(huì)重新渲染。否則,它將跳過渲染并返回上一次渲染的結(jié)果。

繼續(xù)舉例子:

import React, { memo } from "react";
const ChildComponent = memo(function ChildComponent({ text }) {
  console.log("ChildComponent rendered");
  return <div>{text}</div>;
});
function ParentComponent({ showChild }) {
  return (
    <div>
      {showChild && <ChildComponent text="Hello, world!" />}
      <button onClick={() => setShowChild(!showChild)}>Toggle child</button>
    </div>
  );
}

在這個(gè)例子中,我們創(chuàng)建了一個(gè)名為 ChildComponent 的組件,并使用 memo 高階組件對(duì)其進(jìn)行了優(yōu)化。我們還創(chuàng)建了一個(gè)名為 ParentComponent 的組件,它可以切換 ChildComponent 的顯示。當(dāng) ParentComponent 重新渲染時(shí),ChildComponent 的屬性沒有發(fā)生變化,因此它不會(huì)重新渲染。

區(qū)別

用法都很清楚了,接下來總結(jié)一下它們之間的區(qū)別:

  • useMemo 用于避免在組件重新渲染時(shí)執(zhí)行昂貴的計(jì)算,只有在依賴發(fā)生變化時(shí)重新計(jì)算值。
  • useCallback 用于避免在組件重新渲染時(shí)創(chuàng)建新的函數(shù)實(shí)例,只有在依賴發(fā)生變化時(shí)返回新的函數(shù)實(shí)例。
  • memo 用于避免在父組件重新渲染時(shí)重新渲染子組件,只有在屬性發(fā)生變化時(shí)重新渲染組件。

雖然這些功能都可以幫助我們優(yōu)化性能,但它們的使用場(chǎng)景和工作原理有所不同。在實(shí)際開發(fā)中,需要因地制宜合理選用。

源碼分析

為了更深入地了解 useMemouseCallbackmemo 的工作原理,我們將繼續(xù)分析 React 18 的源碼。我們將關(guān)注這些功能的核心邏輯,并詳細(xì)解釋它們的功能。

調(diào)度器

眾所周知,在React hooks的體系中,每個(gè)鉤子都有自己各個(gè)階段的執(zhí)行邏輯,并且存到對(duì)應(yīng)的Dispatcher中。

就拿useMemo來舉例:

// 掛載時(shí)的調(diào)度器
const HooksDispatcherOnMount: Dispatcher = {
  // useMemo 掛載時(shí)的執(zhí)行函數(shù)
  useMemo: mountMemo,
  // other hooks...
};
// 數(shù)據(jù)更新時(shí)的調(diào)度器
const HooksDispatcherOnUpdate: Dispatcher = {
  // useMemo 掛載時(shí)的執(zhí)行函數(shù)
  useMemo: updateMemo,
  // other hooks...
};
// 其他生命周期調(diào)度器...

上面代碼可以看出,useMemo 在掛載時(shí)執(zhí)行了的是 mountMemo, 而在更新數(shù)據(jù)時(shí)執(zhí)行的是 updateMemo。但為了更好了解 useMemo、useCallbackmemo 的區(qū)別,我們只看更新部分就足夠了。

useMemo 源碼分析

源碼在packages/react-reconciler/src/ReactFiberHooks.js 中可以找到:

function updateMemo<T>(
  nextCreate: () => T,
  deps: Array<mixed> | void | null,
): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  // Assume these are defined. If they're not, areHookInputsEqual will warn.
  if (nextDeps !== null) {
    const prevDeps: Array<mixed> | null = prevState[1];
    if (areHookInputsEqual(nextDeps, prevDeps)) {
      return prevState[0];
    }
  }
  if (shouldDoubleInvokeUserFnsInHooksDEV) {
    nextCreate();
  }
  const nextValue = nextCreate();
  hook.memoizedState = [nextValue, nextDeps];
  return nextValue;
}

updateMemo 的實(shí)現(xiàn)中,有一個(gè)關(guān)鍵函數(shù) areHookInputsEqual,它用于比較依賴項(xiàng)數(shù)組:

function areHookInputsEqual(
  nextDeps: Array<mixed>,
  prevDeps: Array<mixed> | null,
): boolean {
  if (__DEV__) {
    if (ignorePreviousDependencies) {
      // Only true when this component is being hot reloaded.
      return false;
    }
  }
  if (prevDeps === null) {
    if (__DEV__) {
      console.error(
        '%s received a final argument during this render, but not during ' +
          'the previous render. Even though the final argument is optional, ' +
          'its type cannot change between renders.',
        currentHookNameInDev,
      );
    }
    return false;
  }
  if (__DEV__) {
    // Don't bother comparing lengths in prod because these arrays should be
    // passed inline.
    if (nextDeps.length !== prevDeps.length) {
      console.error(
        'The final argument passed to %s changed size between renders. The ' +
          'order and size of this array must remain constant.\n\n' +
          'Previous: %s\n' +
          'Incoming: %s',
        currentHookNameInDev,
        `[${prevDeps.join(', ')}]`,
        `[${nextDeps.join(', ')}]`,
      );
    }
  }
  // $FlowFixMe[incompatible-use] found when upgrading Flow
  for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
    // $FlowFixMe[incompatible-use] found when upgrading Flow
    if (is(nextDeps[i], prevDeps[i])) {
      continue;
    }
    return false;
  }
  return true;
}

areHookInputsEqual 函數(shù)接受兩個(gè)依賴項(xiàng)數(shù)組 nextDepsprevDeps。它首先檢查兩個(gè)數(shù)組的長(zhǎng)度是否相等,如果不相等,將在開發(fā)模式下發(fā)出警告。然后,它遍歷數(shù)組并使用 is 函數(shù)(類似于 Object.is)逐個(gè)比較元素。如果發(fā)現(xiàn)任何不相等的元素,函數(shù)將返回 false。否則,返回 true。

這個(gè)函數(shù)在 useMemo 的實(shí)現(xiàn)中起到了關(guān)鍵作用,因?yàn)樗鼪Q定了是否需要重新計(jì)算值。如果依賴項(xiàng)數(shù)組相等,useMemo 將返回上一次計(jì)算的值;否則,它將執(zhí)行 nextCreate 函數(shù)并返回一個(gè)新的值。

useCallback 源碼分析

由于 useCallbackuseMemo 實(shí)現(xiàn)一致,其原理都是通過areHookInputsEqual 函數(shù)進(jìn)行依賴項(xiàng)比對(duì),區(qū)別在于 useMemo 返回是新數(shù)據(jù)對(duì)象,而 useCallback 返回是回調(diào)函數(shù)。源碼如下:

function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (nextDeps !== null) {
    const prevDeps: Array<mixed> | null = prevState[1];
    if (areHookInputsEqual(nextDeps, prevDeps)) {
      return prevState[0];
    }
  }
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

memo 源碼分析

memo 的實(shí)現(xiàn)中,有一個(gè)關(guān)鍵函數(shù) updateMemoComponent,它用于更新 memo 組件。這個(gè)函數(shù)位于 packages/react-reconciler/src/ReactFiberBeginWork.js 文件中:

function updateMemoComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  updateLanes: Lanes,
  renderLanes: Lanes,
): null | Fiber {
  if (current !== null) {
    // ...
    const prevProps = current.memoizedProps;
    const compare = Component.compare;
    const compareFn = compare !== null ? compare : shallowEqual;
    if (compareFn(prevProps, nextProps)) {
      return bailoutOnAlreadyFinishedWork(
        current,
        workInProgress,
        renderLanes,
      );
    }
  }
  // ...render the component and return the result
}

updateMemoComponent 函數(shù)首先檢查當(dāng)前組件是否具有上一次的屬性 prevProps。如果存在,它將獲取 memo 組件的比較函數(shù) compare。如果沒有提供比較函數(shù),React 將使用默認(rèn)的淺比較函數(shù) shallowEqual。

接下來,React 使用比較函數(shù)來檢查上一次的屬性 prevProps 是否與新的屬性 nextProps 相等。如果相等,React 將調(diào)用 bailoutOnAlreadyFinishedWork 函數(shù)來阻止組件重新渲染。否則,它將繼續(xù)渲染組件并返回結(jié)果。

bailoutOnAlreadyFinishedWork 函數(shù)的實(shí)現(xiàn)位于同一個(gè)文件中,它的核心邏輯如下:

    function bailoutOnAlreadyFinishedWork(
      current: Fiber | null,
      workInProgress: Fiber,
      renderLanes: Lanes,
    ): null | Fiber {
      if (current !== null) {
        // Reuse previous dependencies
        workInProgress.dependencies = current.dependencies;
      }
      // ...some code
      // Check if the children have any pending work
      if ((workInProgress.childLanes & renderLanes) !== NoLanes) {
        // ...some code
      } else {
        // The children don't have any work. Set the bailout state.
        workInProgress.lanes = NoLanes;
        workInProgress.childLanes = NoLanes;
        return null;
      }
      // ...some code
    }

bailoutOnAlreadyFinishedWork 函數(shù)首先復(fù)用上一次的依賴項(xiàng)。然后,它檢查子組件是否有任何待處理的工作。如果沒有,它將設(shè)置 workInProgress.lanesworkInProgress.childLanesNoLanes,并返回 null,從而阻止組件重新渲染。

總結(jié)

在這篇文章中,我們深入分析了 React 18 中的 useMemo、useCallbackmemo 功能的源碼。希望這篇文章能幫助你更好在實(shí)際項(xiàng)目中應(yīng)用它們。

以上就是深入React 18源碼useMemo useCallback memo用法及區(qū)別分析的詳細(xì)內(nèi)容,更多關(guān)于React useMemo useCallback memo的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解關(guān)于React-Router4.0跳轉(zhuǎn)不置頂解決方案

    詳解關(guān)于React-Router4.0跳轉(zhuǎn)不置頂解決方案

    這篇文章主要介紹了詳解關(guān)于React-Router4.0跳轉(zhuǎn)不置頂解決案,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-05-05
  • React Hooks與setInterval的踩坑問題小結(jié)

    React Hooks與setInterval的踩坑問題小結(jié)

    本文主要介紹了React Hooks與setInterval的踩坑,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 深入理解React 三大核心屬性

    深入理解React 三大核心屬性

    本文主要介紹了React 三大核心屬性,主要包括State屬性,Props屬性,Refs屬性,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 使用VSCode Debugger調(diào)試React項(xiàng)目的實(shí)現(xiàn)步驟

    使用VSCode Debugger調(diào)試React項(xiàng)目的實(shí)現(xiàn)步驟

    本文主要介紹了使用VSCode Debugger調(diào)試React項(xiàng)目的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-09-09
  • React中傳遞組件的三種方式小結(jié)

    React中傳遞組件的三種方式小結(jié)

    通過傳遞組件,我們可以將復(fù)雜組件內(nèi)部的一部分 UI 交由外部組件來控制渲染,這也是控制反轉(zhuǎn)(Inversion of Control)的一種體現(xiàn),在 React 中,我們可以通過三種方式來傳遞組件,本文就來給大家述說這三種方式,需要的朋友可以參考下
    2023-07-07
  • React?Suspense解決競(jìng)態(tài)條件詳解

    React?Suspense解決競(jìng)態(tài)條件詳解

    這篇文章主要為大家介紹了React?Suspense解決競(jìng)態(tài)條件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • react-native 配置@符號(hào)絕對(duì)路徑配置和絕對(duì)路徑?jīng)]有提示的問題

    react-native 配置@符號(hào)絕對(duì)路徑配置和絕對(duì)路徑?jīng)]有提示的問題

    本文主要介紹了react-native 配置@符號(hào)絕對(duì)路徑配置和絕對(duì)路徑?jīng)]有提示的問題,文中通過圖文示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • React基礎(chǔ)-JSX的本質(zhì)-虛擬DOM的創(chuàng)建過程實(shí)例分析

    React基礎(chǔ)-JSX的本質(zhì)-虛擬DOM的創(chuàng)建過程實(shí)例分析

    這篇文章主要介紹了React基礎(chǔ)-JSX的本質(zhì)-虛擬DOM的創(chuàng)建過程,結(jié)合具體實(shí)例形式分析了虛擬dom的基本原理與實(shí)現(xiàn)方法,需要的朋友可以參考下
    2023-05-05
  • react用Redux中央倉(cāng)庫(kù)實(shí)現(xiàn)一個(gè)todolist

    react用Redux中央倉(cāng)庫(kù)實(shí)現(xiàn)一個(gè)todolist

    這篇文章主要為大家詳細(xì)介紹了react用Redux中央倉(cāng)庫(kù)實(shí)現(xiàn)一個(gè)todolist,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • react遞歸組件實(shí)現(xiàn)樹的示例詳解

    react遞歸組件實(shí)現(xiàn)樹的示例詳解

    在一些react項(xiàng)目中,常常有一些需要目錄樹這種結(jié)構(gòu),這篇文章主要為大家介紹了如何使用遞歸組件實(shí)現(xiàn)樹,感興趣的小伙伴可以了解下
    2024-10-10

最新評(píng)論

蒙城县| 汕头市| 东乡族自治县| 新民市| 富阳市| 商都县| 朔州市| 山丹县| 靖远县| 香河县| 于田县| 龙胜| 桐梓县| 西平县| 文成县| 维西| 英吉沙县| 彭水| 云浮市| 汉寿县| 容城县| 营山县| 平阳县| 山阳县| 商城县| 博湖县| 江西省| 出国| 个旧市| 北票市| 射洪县| 新源县| 嘉善县| 大悟县| 图们市| 保靖县| 奇台县| 峨边| 土默特右旗| 轮台县| 辛集市|