尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

React 性能优化:将昂贵计算提取到 memo 化组件中(rerender-memo 规则详解)

React 性能优化:将昂贵计算提取到 memo 化组件中(rerender-memo 规则详解) 【免费下载链接】open-slideA slide framework built for agents.项目地址https://gitcode.com/gh_mirrors/op/open-slide点击查看免费下载导读在 React 应用中组件每次因状态或父组件更新而重渲染时其函数体都会重新执行。如果函数体内直接嵌入了昂贵的计算如头像 ID 推导、列表排序、SVG 数据合成等即使计算结果最终没有被渲染这些计算也会白白消耗 CPU。本文基于 open-slide 仓库.agents/skills/vercel-react-best-practices技能集中的rerender-memo规则原文讲解如何把昂贵工作提取到memo()化组件中利用**提前 returnearly return**让加载态、空态等分支路径彻底跳过计算并给出可直接复制的修正前后代码、适用边界与 React Compiler 时代的取舍建议。读完本文你将掌握一条适用于任何 React/Next.js 项目的通用重构套路并能结合仓库内的真实源码理解其落点。一、规则定位Re-render Optimization 家族的一员在 Vercel 维护的这套最佳实践中共收录 70 条规则、按影响级别划分为 8 大类别见 .agents/skills/vercel-react-best-practices/SKILL.md优先级类别影响前缀1消除 WaterfallCRITICALasync-2包体积优化CRITICALbundle-3服务端性能HIGHserver-4客户端数据获取MEDIUM-HIGHclient-5重渲染优化MEDIUMrerender-6渲染性能MEDIUMrendering-7JavaScript 性能LOW-MEDIUMjs-8高级模式LOWadvanced-rerender-memo属于第 5 类「重渲染优化」定位为 MEDIUM 影响。该规则在技能集的 Quick Reference 中被一句话概括为Extract expensive work into memoized components把昂贵工作提取进 memo 化组件其 frontmatter 中标注的impactDescription是enables early returns——即本规则的核心收益是让早退分支成为可能。与它同族的兄弟规则还包括rerender-memo-with-default-value把非原始类型的默认 props 提升为模块级常量避免每次渲染都新建引用导致 memo 失效rerender-simple-expression-in-memo对简单原始值表达式不要过度使用 memorerender-no-inline-components不要在组件内部定义组件否则每次渲染都是新组件类型memo 完全失效rerender-split-combined-hooks把依赖相互独立的 hooks 拆开缩小重算范围。二、问题代码useMemo 兜不住「未渲染的昂贵计算」先看规则给出的反面示例// ❌ 错误写法即使 loading 时也会计算 avatar function Profile({ user, loading }: Props) { const avatar useMemo(() { const id computeAvatarId(user) return Avatar id{id} / }, [user]) if (loading) return Skeleton / return div{avatar}/div }这段代码的问题不在于useMemo本身而在于放置的位置。useMemo的回调会在组件函数体的执行过程中求值而当loading为 true 时虽然最终会走return Skeleton /提前返回但avatar这一行已经在早退之前执行完毕。也就是说computeAvatarId(user)这笔昂贵计算每次渲染都照算不误计算结果Avatar /在 loading 阶段根本不会被渲染属于被白白浪费的 CPU 周期useMemo只是帮你缓存结果并不能帮你跳过计算——只要组件重渲染函数体内的缓存代码仍会执行只有依赖未变时才会命中缓存值。一句话总结useMemo阻止的是「重复使用旧结果」而本场景真正需要的是「根本不进入计算路径」。三、修正写法memo 化组件 提前 return规则给出的正确写法如下// ✅ 正确写法loading 分支直接早退完全跳过计算 const UserAvatar memo(function UserAvatar({ user }: { user: User }) { const id useMemo(() computeAvatarId(user), [user]) return Avatar id{id} / }) function Profile({ user, loading }: Props) { if (loading) return Skeleton / return ( div UserAvatar user{user} / /div ) }重构带来的三个关键变化计算被搬进独立的子组件computeAvatarId不再在Profile的函数体内执行而是成为UserAvatar组件内部的逻辑。Profile重渲染时UserAvatar的函数体根本不会被调用因为渲染 JSX 时它还没有进入该分支。early return 成为可能if (loading) return Skeleton /现在真正起到了「短路」作用——loading 期间不仅不渲染Avatar连computeAvatarId也一次都不会执行。这正是 frontmatter 中impactDescription: enables early returns的落地含义。memo 提供第二道防线当loading从 true 变为 false、Profile首次渲染出UserAvatar user{user} /后如果后续Profile因其他状态如别的 UI 状态重渲染而user引用未变memo会跳过UserAvatar的重渲染连带跳过useMemo的重复计算。可以这样理解这条规则的定位memo 化组件负责「减少重渲染次数」early return 负责「在必渲染的路径上缩短函数体执行量」二者组合才能既省渲染、又省计算。四、配套要点让 memo 真正生效的三个前提提取 memo 化组件只是第一步下面三条直接决定优化是否落地同属本技能集的配套规则1. props 必须是「稳定的引用」——见rerender-memo-with-default-valuememo()的默认比较方式是浅比较Object.is。如果父组件每次渲染都传入一个新对象字面量如style{{ ... }}、内联数组memo 的浅比较永远失败组件照样重渲染。解法是把默认的非原始值提升到模块顶层// ❌ 每次渲染都会产生新对象memo 失效 Panel options{{ showHeader: true }} / // ✅ 模块级常量引用稳定 const PANEL_OPTIONS { showHeader: true } Panel options{PANEL_OPTIONS} /2. 不要「组件内定义组件」——见rerender-no-inline-components在组件函数体内定义子组件或传入内联函数组件每次渲染都会创建新的组件类型React 会将其视为不同组件而强制卸载/重挂载memo形同虚设。应始终在模块顶层定义组件。3. 依赖项保持原始值——见rerender-dependenciesuseMemo/useEffect的依赖数组里如果放对象、数组等非原始值引用一变就会重算。尽量让依赖是原始值字符串、数字、布尔或先用 memo 稳定引用。五、仓库内真实对应open-slide 中的 memo 化实践这套规则在 open-slide 的源码中并非空谈以下是几处可直接对照的实现证据。5.1NoteMarkdown昂贵的 Markdown 解析被缓存在 packages/core/src/app/components/note-markdown.tsx 中演讲者备注的 Markdown 解析结果被useMemo缓存export function NoteMarkdown({ text }: { text: string }) { const blocks useMemo(() parseNoteMarkdown(text), [text]); return div classNamespace-y-[0.65em] whitespace-pre-wrap{blocks.map(renderBlock)}/div; }这里parseNoteMarkdown(text)是一次典型的「纯函数 昂贵解析」组合只要text未变解析结果直接命中缓存renderBlock的映射也只作用于同一份 blocks 引用避免了重复解析与重复创建子元素。5.2 资源管理器派生数据在渲染期通过 useMemo 计算在 packages/core/src/app/components/asset-view.tsx 中可见资源列表的排序/过滤结果、以及资源名集合都在渲染期用useMemo推导const deferredQuery useDeferredValue(query); const visibleAssets useMemo( () sortAssets( filterAssets(assets, { usage: usageFilter, type: typeFilter, search: deferredQuery, }), { key: sort.key, direction: sort.direction }, ), [assets, deferredQuery, sort.direction, sort.key, typeFilter, usageFilter], ); const existingNames useMemo(() new Set(assets.map((asset) asset.name)), [assets]);值得注意的细节是它同时用到了useDeferredValue对应同族规则rerender-use-deferred-value搜索输入时先以延迟值参与过滤保证输入框本身不卡顿。而visibleAssets的依赖数组精确列出了全部参与计算的输入任何未列出的值变化都不会触发重算。5.3 加载态 UI 的 early return 形态在 asset-view.tsx 的 SVG 图标卡片中pending正在下载状态通过 JSX 条件渲染直接切换为加载图标而不是先执行完整卡片逻辑再决定显示什么{pending ? ( Loader2 classNamesize-3 animate-spin motion-reduce:animate-none / ) : ( t.common.add )}这与本规则的核心思想同源把「昂贵/无关的工作」放在条件分支之外只让必要路径执行必要代码。同理如果你把「根据 pending 状态显示按钮内容」的整段逻辑放在组件顶部早退效果更彻底。说明以上源码示例展示的是 open-slide 对useMemo/条件渲染的既有实践rerender-memo规则主张的「memo 化组件 early return」组合在编写幻灯片组件、审查性能时可直接套用同一思路。六、边界与取舍什么时候不该用这条规则1. React Compiler 已启用时手动 memo 可省略规则原文明确给出了重要提示Note:If your project has React Compiler enabled, manual memoization withmemo()anduseMemo()is not necessary. The compiler automatically optimizes re-renders.即如果你的项目启用了 React Compiler编译器会自动对组件做 memo 化与依赖分析手动写memo()/useMemo()反而增加阅读负担此时应依赖编译器并保持代码简洁。但请注意即使有编译器把昂贵计算放进「能早退的子组件」这一结构调整依然有价值——编译器优化的是重渲染而 early return 优化的是单次渲染的函数体执行路径两者解决的问题不同。2. 计算本身廉价时不值得提取同族规则rerender-simple-expression-in-memo提醒对user.firstName user.lastName这类纯原始值拼接提取 memo 化组件反而引入结构噪音收益为负。本规则针对的是「昂贵」计算——需要判断computeAvatarId这类推导是否真的昂贵大数组排序/过滤、复杂字符串解析、正则匹配、图片 base64 合成等才值得动用。3. 不是所有组件都适合 memo如果某组件几乎每次渲染 props 都会变化如统计图表数据memo 浅比较成本 可能存在的失效场景会让优化形同虚设。优先对「父级高频重渲染、子级 props 低频变化」的组件使用。七、实操清单与自查要点把本规则落地为可执行的检查项定位在组件函数体内找出所有useMemo/直接计算的昂贵表达式特别是那些位于if (loading) return ...等早退分支之前的判断该计算结果是否只在特定分支使用如果是提取为独立 memo 化子组件重构把计算移入子组件父组件提前 return 加载态/空态验证确认子组件 props 引用稳定无内联对象/函数未在组件内定义组件useMemo依赖数组精确完整回归确认启用了 React Compiler 的项目无需手动 memo避免过度优化。遵循此流程你可以把「加载时白算一笔」这类隐性浪费逐处消除让组件的每次渲染都只执行真正需要的结果。更多同族规则如rerender-memo-with-default-value、rerender-simple-expression-in-memo、rerender-no-inline-components可在 .agents/skills/vercel-react-best-practices/rules 目录下逐一查阅完整技能说明见 .agents/skills/vercel-react-best-practices/SKILL.md规则分类总览见 .agents/skills/vercel-react-best-practices/rules/_sections.md。赞分享【免费下载链接】open-slideA slide framework built for agents.项目地址https://gitcode.com/gh_mirrors/op/open-slide点击查看免费下载相关推荐Langfuse Web 前端中的 React 重渲染优化将昂贵计算提取为 memo 组件rerender-memo 规则详解Langfuse Web 前端中的 React 重渲染优化将昂贵计算提取为 memo 组件rerender memo 规则详解 Langfuse 是一个开人工智能LLMOps可观测性AI 评测LLM 网关后端前端OpenMetadata UI 性能优化将昂贵计算提取为 Memo 组件以跳过重渲染OpenMetadata UI 性能优化将昂贵计算提取为 Memo 组件以跳过重渲染 导读 本文聚焦 OpenMetadata 前端 openmetadat数据目录数据血缘数据治理后端MCP 服务Sanity 仓库内置的 Vercel React 最佳实践:rerender-memo 规则深度解析——把昂贵计算抽成 Memo 化组件,让提前返回真正生效Sanity 仓库内置的 Vercel React 最佳实践:rerender memo 规则深度解析——把昂贵计算抽成 Memo 化组件,让提前返回真正生效CMS前端上一篇Argilla 服务器多环境部署指南Docker Compose 与 Kubernetes Helm Chart 实战下一篇Zotero 插件市场实操指南在 Zotero 内浏览、安装与更新插件的完整流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表