Plate 性能规则实战:用 SWR 订阅去重全局事件监听器,让 N 个组件共享 1 个监听

发布时间:2026/9/14 17:45:13

Plate 性能规则实战:用 SWR 订阅去重全局事件监听器,让 N 个组件共享 1 个监听 Plate 性能规则实战用 SWR 订阅去重全局事件监听器让 N 个组件共享 1 个监听【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate本文讲解 Plate 仓库内置 Vercel React 最佳实践技能中的一条客户端性能规则——client-event-listeners去重全局事件监听器。它解决的问题是当同一个自定义 Hook如键盘快捷键、视口监听被 N 个组件实例调用时浏览器上会挂载 N 个全局事件监听器读完本文你将掌握模块级回调注册表 useSWRSubscription共享订阅这一标准改造模式并能对照本仓库中真实存在的全局监听 Hook如视口、触摸设备检测、热键判断哪些代码适合应用该规则。规则来源vercel-react-best-practices 技能的规则体系这条规则位于仓库内的 client-event-listeners.md属于vercel-react-best-practices技能的一部分。该技能由 SKILL.md 描述是 Vercel Engineering 维护的一套 React/Next.js 性能优化指南共包含 69 条规则、8 个类别并按影响程度impact划分优先级优先级类别影响级别文件前缀1消除请求瀑布流CRITICALasync-2包体积优化CRITICALbundle-3服务端性能HIGHserver-4客户端数据获取MEDIUM-HIGHclient-5重渲染优化MEDIUMrerender-6渲染性能MEDIUMrendering-7JavaScript 性能LOW-MEDIUMjs-8高级模式LOWadvanced-本条规则的元数据frontmatter完整继承自原规则文件titleDeduplicate Global Event Listeners去重全局事件监听器impactLOWimpactDescription为 single listener for N componentsN 个组件只挂 1 个监听器tagsclient, swr, event-listeners, subscription从client-前缀可以看到它归属第 4 类客户端数据获取。按 SKILL.md 的说法每条规则文件都包含为什么重要、错误示例、正确示例、补充上下文四部分这条规则的核心主张只有一句话使用useSWRSubscription()在多个组件实例之间共享同一个全局事件监听器。规则库本身还带一套构建机制README.md 说明可通过pnpm build将rules/下的规则文件编译成AGENTS.md汇总文档、pnpm validate校验规则文件、pnpm extract-tests抽取 LLM 评测用例文件名前缀决定所属章节规则在章节内按标题字母序排序编号自动生成为。impact 等级从CRITICAL到LOW共六级本文这条被定为LOW——意思是它是渐进式优化通常不是瓶颈主因但在编辑器这类全局监听密集的长页面中值得规范。问题模式N 个实例 N 个全局监听器原规则文件给出的反例是一个典型的useKeyboardShortcutHookfunction useKeyboardShortcut(key: string, callback: () void) { useEffect(() { const handler (e: KeyboardEvent) { if (e.metaKey e.key key) { callback() } } window.addEventListener(keydown, handler) return () window.removeEventListener(handler) // 注意原规则文件此处为 removeEventListener(keydown, handler) }, [key, callback]) }以上为原文件代码的忠实呈现其清理函数写作window.removeEventListener(keydown, handler)本文不改动语义。这段代码的问题在于useEffect在每个组件实例挂载时都会执行一次window.addEventListener(keydown, handler)。如果 10 个组件各自调用useKeyboardShortcutwindow上就会同时挂着 10 个keydown监听器每次按键都要逐一触发、逐一做键名比较。这种每实例一个监听器的模式在本仓库中是真实存在的。从源码结构看几处现有实现都属于该模式use-viewport.tsuseViewportHook 在useEffect中对window.addEventListener(resize, handleResize)注册监听每个使用该 Hook 的组件实例都会独立挂一个resize监听use-is-touch-device.ts同样在 effect 中注册window.addEventListener(resize, onResize)并调用一次初始化多个消费方即多个监听器useHotkeys.tsPlate 的热键底层实现每次useHotkeys调用都会对domNode默认为document执行addEventListener(keydown, handleKeyDown)和addEventListener(keyup, handleKeyUp)见该文件第 210-213 行并在卸载时成对移除。这类监听器单个开销很小这正是该规则 impact 被标为LOW的原因但当编辑器应用里成百上千个节点组件如 Plate 的表格单元格、列表项各自挂监听时事件触发路径会被拉长去重就有意义了。解决方案模块级回调注册表 useSWRSubscription 共享订阅原规则文件给出的正确写法完整继承import useSWRSubscription from swr/subscription // Module-level Map to track callbacks per key const keyCallbacks new Mapstring, Set() void() function useKeyboardShortcut(key: string, callback: () void) { // Register this callback in the Map useEffect(() { if (!keyCallbacks.has(key)) { keyCallbacks.set(key, new Set()) } keyCallbacks.get(key)!.add(callback) return () { const set keyCallbacks.get(key) if (set) { set.delete(callback) if (set.size 0) { keyCallbacks.delete(key) } } } }, [key, callback]) useSWRSubscription(global-keydown, () { const handler (e: KeyboardEvent) { if (e.metaKey keyCallbacks.has(e.key)) { keyCallbacks.get(e.key)!.forEach(cb cb()) } } window.addEventListener(keydown, handler) return () window.removeEventListener(keydown, handler) }) } function Profile() { // Multiple shortcuts will share the same listener useKeyboardShortcut(p, () { /* ... */ }) useKeyboardShortcut(k, () { /* ... */ }) // ... }这个实现由三层组成逐层拆解如下1. 模块级回调注册表。const keyCallbacks new Mapstring, Set() void()声明在组件和 Hook 之外属于模块作用域的单例。它以按键为 keyvalue 是订阅了该按键的回调集合用Set保证同一回调重复注册时不会重复触发。组件挂载时把自己的callback加入集合卸载时从集合中删除当某个按键的集合清空时顺手keyCallbacks.delete(key)把空桶移除避免注册表随会话无限膨胀——这是注册表式架构里防止内存泄漏的关键清理细节。2. 共享订阅键global-keydown。useSWRSubscription(key, setupFn)来自 SWR 的订阅 APIsetupFn返回一个清理函数SWR 保证同一key的订阅在首个订阅者挂载时执行一次 setup、在最后一个订阅者卸载时执行一次 cleanup。因此无论多少组件调用useKeyboardShortcutwindow.addEventListener(keydown, handler)只会真正执行一次——这就是N instances 1 listener的机制来源。对比反例中每次挂载都 addEventListener共享订阅把监听器数量从 N 收敛到 1。3. 单一 handler 内部分发。唯一的监听器收到按键事件后先做廉价的e.metaKey keyCallbacks.has(e.key)判断Map.has是 O(1) 查找这与同技能库中 js-set-map-lookups.md 提倡的用 Set/Map 做 O(1) 查找思想一致再遍历对应Set逐个调用回调。事件触发的路径从N 个监听器各自比较键名变成1 个监听器一次查表 批量分发。需要注意的适用前提该方案依赖 SWR 库swr/subscription是其官方子路径导出。从当前仓库的主package.json、apps/www/package.json中检索不到swr依赖即本仓库源码目前并未引入 SWR这条规则在本仓库的定位是 Vercel 最佳实践技能库沉淀下来的改造准则当项目引入 SWR、或在自建订阅基础设施时可参照该模式落地。与同规则族及现有实现的对照与client-swr-dedup的关系。同目录下有姊妹规则 client-swr-dedup.mdimpact: MEDIUM-HIGH讲的是用useSWR让多个组件实例共享同一份数据请求还给出了useSWRMutation的变更场景示例。两条规则的共同内核是多个实例、单一信源一个作用于网络请求的去重一个作用于 DOM 事件订阅的去重。理解了useSWR的请求去重语义useSWRSubscription的订阅去重就是同一机制在长连接/监听器场景的延伸。与 Plate 热键实现的对照。从源码结构看useHotkeys.ts 代表了另一条工程路线它不共享全局监听器而是通过cbRef见该文件第 84-86 行让回调引用稳定——监听器只在keys/options/scopes变化时重新挂载而回调本身通过 ref 保持最新从而减少 effect 重跑。也就是说它优化的是监听器重挂载的频率而本文规则优化的是监听器实例的总数。两者并不冲突一个编辑器应用可以既有稳定的每实例监听热键需要按元素/作用域精确触发如enableOnContentEditable、scopes等选项都依赖逐实例判断也可以对真正全局的监听如CmdK 打开命令面板采用共享订阅去重。实践要点与限制回调身份要稳定。正确示例中注册 effect 的依赖是[key, callback]。若调用方传入内联匿名函数如useKeyboardShortcut(p, () doSomething())每次渲染都会产生新回调引用导致注册/注销 effect 反复执行——此时应配合useCallback或把最新回调存入 ref注册表里只登记一个稳定句柄。SSR 边界。window只在浏览器存在注册表读写与addEventListener都必须发生在 effect/订阅 setup 内客户端。useSWRSubscription的 setup 函数天然只在客户端执行符合 Next.js SSR/CSR 混合场景这也是该规则 tags 中带client的原因。共享键要按语义划分。示例用global-keydown一个键共享所有快捷键。如果未来需要区分 keydown/keyup 或 modifier 组合通道可以用不同订阅键如global-keyup分别共享而不是退回每实例监听。不要过度套用。该规则 impact 为LOW对于只有一两个消费方的监听比如单例的 use-viewport.ts注册表 订阅的间接层可能比收益更重它最适合大量组件实例重复订阅同一全局事件的场景如列表/表格中每个子项都要响应全局快捷键或窗口事件。注册表必须成对清理。卸载时set.delete(callback)且空集合整体删除是示例中容易被省略却决定长期稳定性的部分只做 add 不做 delete 会让模块级 Map 成为跨路由的内存泄漏点。延伸阅读路径规则原文含完整错误/正确示例client-event-listeners.md技能总览与 8 类规则优先级SKILL.md规则库结构、构建与校验命令README.md姊妹规则SWR 请求去重client-swr-dedup.md客户端其他规则client-passive-event-listeners.md滚动等事件使用 passive 监听、client-localstorage-schema.md仓库内可对照的每实例监听实现use-viewport.ts、use-is-touch-device.ts、useHotkeys.ts【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/14 17:45:13

DFS算法入门:从全排列到八皇后问题实战解析

1. 为什么DFS是算法入门的必修课 深度优先搜索(DFS)作为算法领域的经典入门技术,其重要性不亚于学习编程时的"Hello World"。我第一次接触DFS是在大二的算法课上,当时教授用走迷宫的比喻来解释这个概念——就像一个人在…

2026/9/14 18:20:17

发票表格检测实战:基于YOLOv8与真实数据集的训练调优

简介:发票表格检测数据集是一份面向YOLO系列目标检测框架的行业数据集,专注于发票文档中表格区域的自动定位与边界框回归,可应用于文档结构识别、财务票据自动化处理、办公文档智能审核以及计算机视觉算法研究等场景。压缩包共1820个文件&…

2026/9/14 18:20:17

2026数字中国创新大赛:数字安全赛道解析与参赛指南

1. 赛事背景与战略意义2026数字中国创新大赛-数字安全赛道的启动,标志着我国在数字化转型关键阶段对安全能力建设的高度重视。作为国家级赛事,该赛道直接呼应《数据安全法》提出的"建立健全数据安全治理体系"要求,为产业界搭建了技…

2026/9/14 18:20:17

从原理到手挖再到工具:Web漏洞发现的系统化学习路线

在Web安全这个圈子里,“脚本小子”这四个字基本上是见面就绕道走的名词。下载一个扫描器,点一下开始扫描,然后把扫出来的东西截图发到群里问“这个洞怎么利用”,这几乎是所有新人踩进去的第一个坑。说实话,我自己也当过…

2026/9/14 18:20:17

英中拼音语料工程:Hadoop+Spark构建结构化词典系统

1. 这不是简单的“英文字母转拼音”——它是一套面向语言计算底层的语料工程系统 你搜“英中拼音”,大概率会跳出一堆在线转换工具:输入“Apple”,输出“ipng”。但今天这个项目标题里藏着的,是完全不同的东西——它不处理单个单词…

2026/9/14 18:20:17

工业IoT数据中枢:Kafka集群搭建、Topic设计与性能调优实战

工业数字化搞到第四篇,终于轮到Kafka了。前几篇我写了IoT设备接入、数据采集、边缘网关这些内容,一直在铺垫一条完整的数据链路。今天这篇笔记的主角Kafka,就是那条链路的“中枢神经系统”——所有设备数据、系统日志、业务事件都得从它这儿过…

2026/9/14 18:15:17

Bigemap Pro图层计算功能解析与应用实践

1. Bigemap Pro图层计算功能概述 Bigemap Pro作为一款专业级地理信息系统软件,其图层计算功能为空间数据处理提供了高效精准的操作手段。在实际工作中,我们经常需要对地图图层进行各种几何运算,比如从一张土地利用图中提取特定区域&#xff0…

2026/9/14 2:17:50

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/14 0:03:22

KCF目标跟踪算法与OTB工程实现:毕业设计实战解析

简介:这是一份基于KCF核相关滤波算法、融合尺度池与抗遮挡处理的目标检测跟踪MATLAB完整源码,主要面向计算机相关专业准备毕业设计、课程设计或期末大作业的学生,也适合需要项目实战练习的初学者。源码在OTB数据集上完成验证,能够…

2026/9/14 0:03:22

语音情感识别实战:Keras实现LSTM、CNN、SVM与MLP多模型对比

简介:面向语音情感识别入门与进阶开发者,这份基于Keras的项目源码完整实现了LSTM、CNN、SVM、MLP四种模型,兼容Python3.8与Keras/TensorFlow2环境。压缩包内含49个文件,大小约70.31MB,主体包括Python脚本、yaml/json配…

2026/9/14 11:59:31

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/14 13:53:59

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/14 11:22:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码