当前位置:首页 > 科技  > 软件

现在停止滥用useMemo吧!

来源: 责编: 时间:2024-06-05 17:46:32 89观看
导读在React应用中过度优化真的是一种噩梦。我们每天都要面对大量无用的优化,这些优化意在使代码变得“神秘”。一些开发人员甚至将 useMemo 和 useCallback 纳入他们的默认风格指南中,以简化事情。不要陷入这种迷思,因为 us

Rm528资讯网——每日最新资讯28at.com

在React应用中过度优化真的是一种噩梦。我们每天都要面对大量无用的优化,这些优化意在使代码变得“神秘”。一些开发人员甚至将 useMemo 和 useCallback 纳入他们的默认风格指南中,以简化事情。不要陷入这种迷思,因为 useMemo 甚至可能会减慢你的应用程序速度!记住,记忆化并不是免费的。Rm528资讯网——每日最新资讯28at.com

在这篇文章中,向你展示大多数开发人员如何过度使用 useMemo 钩子,并提供一些避免这种情况的技巧。当我第一次意识到我犯了这些错误时,我感到非常愚蠢。话不多说,开始吧!Rm528资讯网——每日最新资讯28at.com

为什么我们使用useMemo?

useMemo 是一个允许我们在组件重新渲染之间缓存计算结果的钩子。毫无疑问,它仅用于性能原因,并且应该与其他技术如 React.memo、useCallback、debouncing、并发渲染等一起使用。Rm528资讯网——每日最新资讯28at.com

尽管在某些情况下,这个钩子确实有帮助,并且发挥了重要作用,但大多数开发人员却错误地使用它。他们将每个变量都用 useMemo 包裹起来,希望随机的优化能带来成功。不出所料,这种方法只会导致代码可读性差和内存使用增加。Rm528资讯网——每日最新资讯28at.com

另一个需要记住的重要事项是,useMemo 只有在重新渲染阶段才有价值。在初始化期间,记忆化(memoization)会减慢应用程序的速度,并且这种影响会逐渐累积。这就是我所说的“记忆化并不是免费的”的意思。Rm528资讯网——每日最新资讯28at.com

什么时候它没有用甚至有害?

现在,看看下面的例子。这些都是我从一个项目中拿来的真实代码片段。你能说出哪个useMemo的使用实际上有意义吗?Rm528资讯网——每日最新资讯28at.com

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>  );};

通常我们在两种情况下使用 useMemo:记住一个引用并将其传递给 memo 组件,或缓存一些昂贵的计算。Rm528资讯网——每日最新资讯28at.com

现在想一想,我们在上面的例子中优化了什么?我们有原始值,并没有将任何更深层次的内容传递到组件树中,所以我们不需要保留引用。而且.split和===似乎不是需要记忆化的复杂计算。因此,我们可以轻松地在这个例子中去掉useMemo,节省一些文件空间 :)Rm528资讯网——每日最新资讯28at.com

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。你认为优化tabs创建的主要意图是什么?这根本不是一个计算问题,所以实现它的人可能是想要保留引用并避免 NavTabs 的过度重新渲染。这在这里是正确的做法吗?Rm528资讯网——每日最新资讯28at.com

首先,NavTabs 是一个轻量级组件,可以多次重新渲染而不影响性能。其次,即使它是一个重量级组件,useMemo 也不会起作用。仅仅保留引用不足以防止 NavTabs 在每次Client组件重新渲染时重新渲染。为了优化性能,我们应该用React.memo包裹 NavTabs。Rm528资讯网——每日最新资讯28at.com

memo 函数返回我们组件的记忆化版本。只要它的 props 没有改变,这个版本通常不会在其父组件重新渲染时重新渲染。memo 使用浅比较props来决定组件是否应该更新。如果你有一些特定条件来决定组件何时应该重新渲染,你甚至可以指定自己的比较函数作为 memo 的第二个参数。Rm528资讯网——每日最新资讯28at.com

如何判断计算是否昂贵?

除非你在执行成千上万项复杂循环或计算阶乘,否则它可能不昂贵。你可以结合使用React Profiler和performance.now()来识别瓶颈,然后再应用优化技术。Rm528资讯网——每日最新资讯28at.com

避免在以下情况下使用 useMemo:Rm528资讯网——每日最新资讯28at.com

  • 试图优化的计算很便宜。在这些情况下,使用useMemo的开销将超过其带来的好处。
  • 不确定是否需要记忆化。最好先不使用它,然后在出现问题时逐步将优化引入代码。
  • 记忆化的值没有传递到组件中。如果该值仅在JSX中使用,并且没有传递到组件树中,大多数情况下你可以避免优化。没有必要记住它的引用,因为它不会影响其他组件的重新渲染。
  • 依赖数组变化太频繁。在这种情况下,useMemo不会带来任何性能优势,因为它每次都会重新计算。

如何正确使用的技巧

React 组件每次重新渲染时都会运行其主体,这发生在其道具或状态发生变化时。通常情况下,我们希望避免在渲染过程中进行昂贵的计算,因为它们会减慢组件的运行速度。Rm528资讯网——每日最新资讯28at.com

然而,大多数计算仍然非常快,因此很难理解哪些地方确实需要使用 useMemo。要开始有效且有目的性地进行优化,首先应找出问题所在。此外,不要忘记其他技术,例如:useCallback、React.memo、debouncing、代码拆分、lazy-loading 等。Rm528资讯网——每日最新资讯28at.com

我们看下面一个非常简单的例子。doCalculation 是一个人为放慢速度的函数,因此它需要 500ms 才能返回一个随机数。那么我们在这里遇到了什么问题呢?是的,每次值更新时,我们都要重新计算我们的重函数,这使得使用输入变得非常困难。Rm528资讯网——每日最新资讯28at.com

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>  );}

我们如何解决这个问题?只需用useMemo包裹doCalculation而不加任何依赖项。Rm528资讯网——每日最新资讯28at.com

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

现场实例:Rm528资讯网——每日最新资讯28at.com

https://codesandbox.io/s/happy-grass-0mjpbw?from-embed。Rm528资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-92175-0.html现在停止滥用useMemo吧!

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Spring Boot 性能太差?试试这几招!

下一篇: 项目亮点:分析与解决小程序页面切换过程卡顿的问题

标签:
  • 热门焦点
Top
Baidu
map