首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Plate 的 Vercel React 最佳实践:用模块级 Map 缓存重复函数调用(Cache Repeated Function Calls)
📅 2026/9/14 18:25:42
✍️ 爱科研究院
👁 阅读 3,247
Plate 的 Vercel React 最佳实践用模块级 Map 缓存重复函数调用Cache Repeated Function Calls【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate本文基于 Plate一个内置 AI 能力与 shadcn/ui 的富文本编辑器仓库中的技能规则文件 js-cache-function-results.md 展开。该规则来自仓库内收录的vercel-react-best-practices技能Vercel Engineering 出品的 React/Next.js 性能优化指南属于JavaScript Performance分类下编号 7.4 的规则影响等级 MEDIUM核心目标是消除渲染路径上的冗余计算。读完本文你将掌握模块级 Map 缓存函数结果的完整写法、单值函数的简化缓存模式、缓存失效时机并能在 Plate 的真实源码中看到同一思路的落地形态。一、规则在技能体系中的定位Plate 仓库通过 .agents/skills/vercel-react-best-practices/ 目录收纳了一套面向 Agent 和 LLM 的 React 最佳实践规则库。其组织结构在 README.md 中有明确约定rules/目录存放单条规则文件每条规则对应一个 markdown 文件文件名前缀即分类标识async-消除瀑布流、bundle-包体积、server-服务端性能、client-客户端数据获取、rerender-重渲染优化、rendering-渲染性能、js-JavaScript 性能、advanced-进阶模式分类的元数据集中在 _sections.md 中定义其中第 7 类JavaScript Performance的整体影响等级为 LOW-MEDIUM描述为热点路径上的微优化累积起来也能带来可观收益。本规则即属该类每条规则都按 _template.md 定义的统一结构编写frontmatter 元信息 错误示例 正确示例 参考出处。本规则的 frontmatter 完整声明了它的元信息--- title: Cache Repeated Function Calls impact: MEDIUM impactDescription: avoid redundant computation tags: javascript, cache, memoization, performance ---impact: MEDIUM表明它是一条值得做但不是紧急项的规则impactDescription一句话点明动机——避免冗余计算。规则库的构建流程pnpm build将 rules 编译为 AGENTS.md 供 AI 工作流引用也在 README 中说明本文讨论的规则在编译产物中对应 AGENTS.md 的 7.4 小节。二、规则核心用模块级 Map 缓存重复的函数调用规则原文的核心陈述只有一句话当同一个函数在渲染期间被相同输入反复调用时使用模块级 Map 缓存函数结果。下面完整继承原文的两个对照示例。错误示例渲染中重复计算function ProjectList({ projects }: { projects: Project[] }) { return ( div {projects.map(project { // slugify() called 100 times for same project names const slug slugify(project.name) return ProjectCard key{project.id} slug{slug} / })} /div ) }问题在于组件每次渲染都会对列表中的每一项调用slugify(project.name)。当项目名称存在大量重复例如 100 个项目共享少量命名同一输入的字符串转换被反复执行且 React 的重渲染父组件状态变化、列表增删项会让这个浪费成倍放大。正确示例模块级 Map 缓存// Module-level cache const slugifyCache new Mapstring, string() function cachedSlugify(text: string): string { if (slugifyCache.has(text)) { return slugifyCache.get(text)! } const result slugify(text) slugifyCache.set(text, result) return result } function ProjectList({ projects }: { projects: Project[] }) { return ( div {projects.map(project { // Computed only once per unique project name const slug cachedSlugify(project.name) return ProjectCard key{project.id} slug{slug} / })} /div ) }写法要点有三个缓存放模块作用域而非组件作用域。const slugifyCache new Mapstring, string()声明在组件外整个应用生命周期内只初始化一次跨渲染、跨组件实例共享先查后算check-then-computehasget命中即直接返回未命中才执行真正的slugify(text)并set回填因此每个唯一输入只计算一次对外接口不变组件侧代码从slugify(...)换成cachedSlugify(...)类型签名一致属于零侵入改造。为什么用 Map 而不是 Hook规则最后专门强调了这一点原文Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.如果封装成useMemo之类的 Hook缓存就被限制在 React 组件树内部纯工具函数、事件处理器、模块初始化逻辑都无法使用。而模块级 Map 是纯粹的 JavaScript 能力在任何可执行该模块的代码路径上都生效这正是该规则被归入js-JavaScript Performance而非rerender-Re-render Optimization分类的原因——它优化的是计算本身而不是渲染调度。三、简化模式单值函数的模块级缓存原文给出了一个更轻量的变体当缓存对象是单个值而非输入 → 输出的映射表时不需要 Map一个模块级变量加空值哨兵即可。原文示例完整继承如下let isLoggedInCache: boolean | null null function isLoggedIn(): boolean { if (isLoggedInCache ! null) { return isLoggedInCache } isLoggedInCache document.cookie.includes(auth) return isLoggedInCache } // Clear cache when auth changes function onAuthChange() { isLoggedInCache null }这个模式与 Map 版本的关键差异在**缓存失效invalidation**上Map 版天然按 key 区分输入同一 key 的输入语义稳定则结果永远有效一般无需失效单值版缓存的是某个事实的当前状态如是否登录该状态会被外部改变因此必须显式提供失效入口——原文的onAuthChange()就是把缓存重置回null下一次调用会重新计算。从源码结构看这提示了一个通用原则模块级缓存的可靠程度取决于输入是否可变。对于纯函数如slugify缓存终身有效对于带状态的查询如读取 cookie必须把失效逻辑与状态变更点绑定否则会读到陈旧值。四、Plate 源码中的同类实践佐证该规则的价值不仅在规则库里Plate 编辑器本体的源码中也能找到同思路的落地形态可作为模块级缓存 按 key 失效模式的真实参照插件解析结果的 memoizePlate.slow.tsx 中在开发模式的慢渲染路径上使用lodash/memoize对插件集合的解析结果做记忆化const plugins: SlatePlugins memoize(...)避免每次渲染重复解析同一组插件——这正是重复输入、昂贵计算场景下的缓存应用。输入规则模式的索引 MapcreateInputRules.ts 中构建const byTrigger new Mapstring, CompiledPattern[]()把编译后的输入规则模式按触发字符归类。这是按 key 组织的模块级缓存的典型形态编译一次后续按键触发时直接查表命中而不是每次按键都重新编译正则。选择器缓存与按运行时失效useElementSelector.ts 中维护了一个 selector 缓存对象其失效条件写得很直接if (cache.runtime ! runtime || cache.selector ! memoizedSelector)——运行时实例或选择器函数任一变化就丢弃旧缓存重新计算。这恰好印证了第三节讨论的失效原则缓存键覆盖了所有可能改变结果的因素这里是 runtime 与 selector 两个维度。插件名到插件实例的 MapresolvePlugins.ts 中const pluginMap new Mapstring, SlatePlugin()以插件 key 为键建立映射供后续解析快速定位。这些实现共同说明在富文本编辑器这类渲染路径上反复执行解析、匹配、选择的系统中模块级 Map 缓存不是理论优化而是 Plate 核心链路中实际在用的结构。五、与同族规则的关系何时用哪种缓存手段js-分类下有多条易混淆的缓存/查找规则结合规则文件可以快速辨析适用边界规则文件适用场景与本规则的区别js-cache-function-results.md同一函数被相同输入反复调用计算昂贵缓存的是计算结果key 是函数输入js-index-maps.md循环中反复在数组里find/查找一次建 MapO(n)之后每次查找 O(1)缓存的是查找索引js-set-map-lookups.md重复的成员判断/键值查询强调用 Set/Map 数据结构替代线性扫描js-cache-property-access.md循环内反复访问深层属性链把obj.config.settings.value提升为局部常量不涉及跨调用缓存此外同一技能库的服务端分类里还有两条缓存规则注意作用域差异server-cache-react.mdReact.cache()只在一个请求内去重适合 RSC 中同一请求内的重复计算server-cache-lru.md跨请求共享数据时改用带容量与 TTL 的 LRU 缓存。本规则模块级 Map作用于客户端任意 JS 执行上下文与上述两者不冲突可按运行环境组合使用。六、适用前提与边界把这条 MEDIUM 级规则用对需要守住三个前提纯函数或输入稳定的查询才适合无界缓存。slugify这类纯函数可以长期持有结果依赖外部状态的结果必须配失效逻辑如第三节onAuthChange重置为null否则读到陈旧数据。缓存容量要有意识。原文示例中的Map未设上限适合 key 空间有限的场景如项目名称、cookie 状态。若 key 空间无界如任意 URL 解析应参考 server-cache-lru.md 的 LRU 思路给容量设界避免内存随使用时间线性增长。服务端不要用它存请求数据。同一技能库的 server-no-shared-module-state.md 明确警示服务端渲染可能同进程并发执行把请求级数据放入模块级可变状态会造成跨请求串数据。模块级 Map 在 Node/SSR 侧只应用于静态配置、只读资源或刻意设计的跨请求共享缓存。七、小结与文件索引js-cache-function-results规则给出的是一套非常克制的性能手法不改组件结构、不动渲染调度仅把相同输入重复计算收敛为唯一输入计算一次。模块级 Map 版本覆盖多值映射单值变量版本覆盖全局状态查询两者共同点都是让缓存在任何 JS 执行上下文组件、工具函数、事件处理器中可用。结合 Plate 核心源码中的 memoize、byTrigger索引 Map 与选择器缓存实例可以确认这一模式在真实编辑器链路中已得到验证。相关文件索引规则原文.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md规则库总览与分类定义AGENTS.md、_sections.md、SKILL.md仓库内佐证实现Plate.slow.tsx、createInputRules.ts、useElementSelector.ts、resolvePlugins.ts【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/14 18:25:42
Flipper Zero 餐厅寻呼机 Sub-GHz 信号实战指南:频率边界、固件扩展与信号库全解析
2026/9/14 18:25:42
Kvasir-SEG息肉检测数据集:YOLO格式解析与医疗目标检测实战
2026/9/14 18:25:42
阿里开源Agent框架深度实战:Qwen-Agent从入门到落地
2026/9/14 19:05:47
Hindsight × Vercel AI SDK:为 AI Agent 接入长期记忆的五个即用型工具
2026/9/14 19:05:47
KubeSphere 依赖库解析:cespare/xxhash/v2 高性能 XXH64 哈希库的 API、算法与仓库内实践
2026/9/14 19:05:47
SymPy 光偏振建模:基于 Jones 与 Stokes 演算的偏振光学元件仿真指南
2026/9/14 19:05:47
大模型SFT训练流程代码逐行解析
2026/9/14 19:05:47
Agent Zero _office 插件 Impress 演示文稿技能:ODP/PPTX 幻灯片制品的创建与编辑实战
2026/9/14 19:00:47
ArmorPaint:实时PBR纹理绘制引擎与Git原生工作流
2026/9/14 0:03:40
KCF目标跟踪算法与OTB工程实现:毕业设计实战解析
2026/9/14 0:03:40
Megatron-LM 推理实战指南:基于 Megatron Core 高层 API 的离线推理与 OpenAI 兼容服务
2026/9/14 0:03:40
语音情感识别实战:Keras实现LSTM、CNN、SVM与MLP多模型对比
2026/9/14 7:37:16
拯救者Y7000黑屏故障排查与维修实战指南
2026/9/14 2:50:57
AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
2026/9/14 11:25:37
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化