自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

現(xiàn)在停止濫用useMemo吧!

開(kāi)發(fā) 前端
在這篇文章中,向你展示大多數(shù)開(kāi)發(fā)人員如何過(guò)度使用 useMemo 鉤子,并提供一些避免這種情況的技巧。當(dāng)我第一次意識(shí)到我犯了這些錯(cuò)誤時(shí),我感到非常愚蠢。話不多說(shuō),開(kāi)始吧!

在React應(yīng)用中過(guò)度優(yōu)化真的是一種噩夢(mèng)。我們每天都要面對(duì)大量無(wú)用的優(yōu)化,這些優(yōu)化意在使代碼變得“神秘”。一些開(kāi)發(fā)人員甚至將 useMemo 和 useCallback 納入他們的默認(rèn)風(fēng)格指南中,以簡(jiǎn)化事情。不要陷入這種迷思,因?yàn)?nbsp;useMemo 甚至可能會(huì)減慢你的應(yīng)用程序速度!記住,記憶化并不是免費(fèi)的。

在這篇文章中,向你展示大多數(shù)開(kāi)發(fā)人員如何過(guò)度使用 useMemo 鉤子,并提供一些避免這種情況的技巧。當(dāng)我第一次意識(shí)到我犯了這些錯(cuò)誤時(shí),我感到非常愚蠢。話不多說(shuō),開(kāi)始吧!

為什么我們使用useMemo?

useMemo 是一個(gè)允許我們?cè)诮M件重新渲染之間緩存計(jì)算結(jié)果的鉤子。毫無(wú)疑問(wèn),它僅用于性能原因,并且應(yīng)該與其他技術(shù)如 React.memo、useCallback、debouncing、并發(fā)渲染等一起使用。

盡管在某些情況下,這個(gè)鉤子確實(shí)有幫助,并且發(fā)揮了重要作用,但大多數(shù)開(kāi)發(fā)人員卻錯(cuò)誤地使用它。他們將每個(gè)變量都用 useMemo 包裹起來(lái),希望隨機(jī)的優(yōu)化能帶來(lái)成功。不出所料,這種方法只會(huì)導(dǎo)致代碼可讀性差和內(nèi)存使用增加。

另一個(gè)需要記住的重要事項(xiàng)是,useMemo 只有在重新渲染階段才有價(jià)值。在初始化期間,記憶化(memoization)會(huì)減慢應(yīng)用程序的速度,并且這種影響會(huì)逐漸累積。這就是我所說(shuō)的“記憶化并不是免費(fèi)的”的意思。

什么時(shí)候它沒(méi)有用甚至有害?

現(xiàn)在,看看下面的例子。這些都是我從一個(gè)項(xiàng)目中拿來(lái)的真實(shí)代碼片段。你能說(shuō)出哪個(gè)useMemo的使用實(shí)際上有意義嗎?

export const NavTabs = ({ tabs, className, withExpander }) => {
  const currentMainPath = useMemo(() => {
    return pathname.split("/")[1];
  }, [pathname]);
  const isCurrentMainPath = useMemo(() => {
    return currentMainPath === pathname.substr(1);
  }, [pathname, currentMainPath]);

  return (
    <StyledWrapper>
      <Span fontSize={18}>
        {isCurrentMainPath ? (
          t(currentMainPath)
        ) : (
          <StyledLink to={`/${currentMainPath}`}>
            {t(currentMainPath)}
          </StyledLink>
        )}
      </Span>
    </StyledWrapper>
  );
};

通常我們?cè)趦煞N情況下使用 useMemo:記住一個(gè)引用并將其傳遞給 memo 組件,或緩存一些昂貴的計(jì)算。

現(xiàn)在想一想,我們?cè)谏厦娴睦又袃?yōu)化了什么?我們有原始值,并沒(méi)有將任何更深層次的內(nèi)容傳遞到組件樹(shù)中,所以我們不需要保留引用。而且.split和===似乎不是需要記憶化的復(fù)雜計(jì)算。因此,我們可以輕松地在這個(gè)例子中去掉useMemo,節(jié)省一些文件空間 :)

export const Client = ({ clientId, ...otherProps }) => {
  const tabs = useMemo(
    () => [
      {
        label: t("client withdrawals"),
        path: `/clients/${clientId}/withdrawals`
      },
      ...
    ],
    [t, clientId]
  );
  
  ...
  
  return (
    <>
      ...
      <NavTabs tabs={tabs} />
    </>
  )
}

export const NavTabs = ({ tabs, className, withExpander }) => {
  return (
    <Wrapper className={className} withExpander={withExpander}>
      {tabs.map((tab) => (
        <Item
          key={tab.path}
          to={tab.path}
          withExpander={withExpander}
        >
          <StyledLabel>{tab.label}</StyledLabel>
        </Item>
      ))}
    </Wrapper>
  );
};

在上面的例子中,我們將tabs變量記憶化,然后將其傳遞給 NavTabs。你認(rèn)為優(yōu)化tabs創(chuàng)建的主要意圖是什么?這根本不是一個(gè)計(jì)算問(wèn)題,所以實(shí)現(xiàn)它的人可能是想要保留引用并避免 NavTabs 的過(guò)度重新渲染。這在這里是正確的做法嗎?

首先,NavTabs 是一個(gè)輕量級(jí)組件,可以多次重新渲染而不影響性能。其次,即使它是一個(gè)重量級(jí)組件,useMemo 也不會(huì)起作用。僅僅保留引用不足以防止 NavTabs 在每次Client組件重新渲染時(shí)重新渲染。為了優(yōu)化性能,我們應(yīng)該用React.memo包裹 NavTabs。

memo 函數(shù)返回我們組件的記憶化版本。只要它的 props 沒(méi)有改變,這個(gè)版本通常不會(huì)在其父組件重新渲染時(shí)重新渲染。memo 使用淺比較props來(lái)決定組件是否應(yīng)該更新。如果你有一些特定條件來(lái)決定組件何時(shí)應(yīng)該重新渲染,你甚至可以指定自己的比較函數(shù)作為 memo 的第二個(gè)參數(shù)。

如何判斷計(jì)算是否昂貴?

除非你在執(zhí)行成千上萬(wàn)項(xiàng)復(fù)雜循環(huán)或計(jì)算階乘,否則它可能不昂貴。你可以結(jié)合使用React Profiler和performance.now()來(lái)識(shí)別瓶頸,然后再應(yīng)用優(yōu)化技術(shù)。

避免在以下情況下使用 useMemo:

  • 試圖優(yōu)化的計(jì)算很便宜。在這些情況下,使用useMemo的開(kāi)銷將超過(guò)其帶來(lái)的好處。
  • 不確定是否需要記憶化。最好先不使用它,然后在出現(xiàn)問(wèn)題時(shí)逐步將優(yōu)化引入代碼。
  • 記憶化的值沒(méi)有傳遞到組件中。如果該值僅在JSX中使用,并且沒(méi)有傳遞到組件樹(shù)中,大多數(shù)情況下你可以避免優(yōu)化。沒(méi)有必要記住它的引用,因?yàn)樗粫?huì)影響其他組件的重新渲染。
  • 依賴數(shù)組變化太頻繁。在這種情況下,useMemo不會(huì)帶來(lái)任何性能優(yōu)勢(shì),因?yàn)樗看味紩?huì)重新計(jì)算。

如何正確使用的技巧

React 組件每次重新渲染時(shí)都會(huì)運(yùn)行其主體,這發(fā)生在其道具或狀態(tài)發(fā)生變化時(shí)。通常情況下,我們希望避免在渲染過(guò)程中進(jìn)行昂貴的計(jì)算,因?yàn)樗鼈儠?huì)減慢組件的運(yùn)行速度。

然而,大多數(shù)計(jì)算仍然非??欤虼撕茈y理解哪些地方確實(shí)需要使用 useMemo。要開(kāi)始有效且有目的性地進(jìn)行優(yōu)化,首先應(yīng)找出問(wèn)題所在。此外,不要忘記其他技術(shù),例如:useCallback、React.memo、debouncing、代碼拆分、lazy-loading 等。

我們看下面一個(gè)非常簡(jiǎn)單的例子。doCalculation 是一個(gè)人為放慢速度的函數(shù),因此它需要 500ms 才能返回一個(gè)隨機(jī)數(shù)。那么我們?cè)谶@里遇到了什么問(wèn)題呢?是的,每次值更新時(shí),我們都要重新計(jì)算我們的重函數(shù),這使得使用輸入變得非常困難。

function doCalculation() {
  const now = performance.now();
  while (performance.now() - now < 500) {
    // 停頓500毫秒
  }

  return Math.random();
}

export default function App() {
  const [value, setValue] = useState(0);
  const calculation = doCalculation();

  return (
    <div>
      <input value={value} onChange={(e) => setValue(e.target.value)} />
      <h1>{`Calculation is ${calculation}`}</h1>
    </div>
  );
}

我們?nèi)绾谓鉀Q這個(gè)問(wèn)題?只需用useMemo包裹doCalculation而不加任何依賴項(xiàng)。

const calculation = useMemo(() => doCalculation(), []);

現(xiàn)場(chǎng)實(shí)例:

https://codesandbox.io/s/happy-grass-0mjpbw?from-embed。

責(zé)任編輯:姜華 來(lái)源: 大遷世界
相關(guān)推薦

2022-03-16 00:07:55

OAuth2授權(quán)框架

2020-12-07 05:50:54

print()Python代碼

2024-05-11 18:48:40

技巧代碼技能

2017-09-11 23:11:00

2023-11-01 17:57:56

React應(yīng)用程序性能

2023-08-04 08:25:16

HookuseMemo

2013-03-21 17:42:35

2021-08-06 09:30:34

人工智能AI人臉識(shí)別

2021-02-02 16:34:12

useMemo性能優(yōu)化函數(shù)

2023-11-22 18:20:14

開(kāi)源項(xiàng)目代碼Python

2012-03-21 21:40:03

Win8

2020-11-12 09:49:29

大數(shù)據(jù)亞馬遜互聯(lián)網(wǎng)

2022-04-28 13:08:51

架構(gòu)師軟件

2022-03-25 11:53:11

Telegram網(wǎng)絡(luò)犯罪分子惡意軟件

2011-07-04 13:36:15

2021-11-02 11:43:59

人臉識(shí)別AI人工智能

2016-12-19 15:54:17

2022-06-15 22:33:07

React逃生艙

2022-06-05 21:27:40

Reacteffect
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)