用途
devtools-instrumentation 用于在库代码库中分析关键架构与调试点,并策略性地添加事件发送(emit)调用,把高价值诊断数据发送到 TanStack Devtools 面板。目标是以最少的噪音获得最大的洞察。
前置条件:先阅读 devtools-event-client 技能,了解 EventClient 创建、事件映射以及 emit()/on() API。
关键洞察
事件总线可透明地跨越服务端/客户端以及跨标签页边界。服务端的 emit() 通过 WebSocket/SSE 到达客户端;一个标签页中的 emit() 通过 BroadcastChannel 到达其他标签页。无需编写传输代码,只需在正确的位置 emit。
原型阶段可以大量抛出事件;生产环境则应整合为数量最少、信息量最大的事件。
在何处埋点
应在架构边界而非实现细节内部发送事件:
- 中间件/拦截器入口与出口——包装整条链,而不是每个中间件
- 状态转换——状态在逻辑阶段之间移动时(idle -> loading -> success/error)
- 生命周期钩子——mount、unmount、connect、disconnect、ready
- 错误边界——捕获的异常、重试、回退
- 用户发起的操作被处理——在完全应用之后,而非之前
不要从以下位置发送事件:内部工具函数、循环迭代、getter/setter 访问、中间计算步骤。
核心模式
1. 中间件/拦截器埋点
在边界处包装整条管道,而不是逐个中间件。示例中定义了 RouterEvents 事件映射与 RouterDevtoolsClient(pluginId 为 'my-router',enabled 依据 NODE_ENV 判断),在 runMiddlewarePipeline 中收集每个中间件的耗时链,最后只发送一个 'request-processed' 事件,包含 id、method、path、duration、middlewareChain、status、error。即每个请求一个事件,而不是 2N 个事件(每个中间件的 start + end)。
2. 状态转换发送
在状态机于阶段之间移动时发送,而不是每次内部变更都发送。示例定义 QueryEvents 的 'query-lifecycle' 事件(queryKey、from、to、data、error、fetchDuration、timestamp),在 Query 类的 transition 方法中,若 from === to 则不发送事件,否则更新状态并 emit。fetch() 中依次 transition('loading')、成功时 transition('success')、失败时 transition('error')。
3. 使用 DRY 载荷的整合事件
当多个事件共享字段时,构建共享基础载荷并展开。示例中 Store 类提供 basePayload()(storeName、version、sessionId、timestamp),dispatch 与 reset 均展开该基础载荷后再附加各自字段。
4. 高频发送的去抖
响应式系统、滚动处理器和流式数据可能每秒触发数百次发送,需要去抖或节流。示例提供 createDebouncedEmitter 工厂函数,内部用 Map 保存定时器,按事件名去抖后调用 client.emit。对 leading+trailing(节流),可用相同模式加上 lastEmit 时间戳检查,在 leading 边缘立即发送。
5. 生产环境防护
enabled: false 是主要防护——emit() 会立即返回,不分配、不排队、不建立连接。对于昂贵的载荷构建(例如大状态的 structuredClone),应在调用点用 process.env.NODE_ENV 检查进行防护。
重要:Vite 插件会从生产构建中剥离 @tanstack/react-devtools。@tanstack/devtools-event-client 的根导入在 process.env.NODE_ENV !== 'development' 时也会 no-op 并被 tree-shake 掉,因此默认情况下 emit() 调用在生产环境没有成本。如果刻意希望在生产环境接收事件,可从 @tanstack/devtools-event-client/production 导入。enabled 选项仍可用于运行时控制。
6. 服务端/客户端透明桥接
同一个 emit() 在服务端和客户端都可用:
- 客户端:在 window 上派发 CustomEvent -> ClientEventBus -> 通过 BroadcastChannel 到其他标签页,通过 WebSocket 到服务端
- 服务端:在 globalThis.TANSTACK_EVENT_TARGET 上派发 -> ServerEventBus -> 所有 WebSocket/SSE 客户端
服务端(例如 SSR 处理器)发送的事件会自动到达浏览器 devtools 面板。
埋点检查清单
- 梳理架构边界(中间件链、状态机、生命周期钩子、错误路径)
- 为每个边界设计一个整合事件,携带完整上下文载荷
- 保持事件映射精简(通常 3-7 种类型,而非 15-30 种)
- 创建 EventClient 时设置 enabled: process.env.NODE_ENV !== 'production'
- 对跨事件共有的字段使用共享基础载荷(DRY)
- 对任何每秒触发超过 10 次的发送点进行去抖
- 用 process.env.NODE_ENV 检查防护昂贵的载荷构建
- 使用 debug: true 测试,查看带 [tanstack-devtools:{pluginId}-plugin] 前缀的日志
常见错误
- 高:发送过多细粒度事件。错误做法是每个请求发送 15 个事件(request-start、middleware-1-start、middleware-1-end 等);正确做法是发送 1 个包含全部数据的事件。来源:维护者访谈。
- 高:在热循环中发送而不去抖。错误做法是 signal.subscribe 中直接 emit(每秒 60+ 次);正确做法是使用 createDebouncedEmitter 去抖。来源:docs/bidirectional-communication.md。
- 中:未在架构边界发送。错误做法是在 parseQueryString 这类辅助函数内部埋点;正确做法是在 handleRequest 处理器边界埋点。来源:维护者访谈。
- 中:硬编码重复的载荷字段。错误做法是在每个事件中重复写 storeName、version、sessionId、timestamp;正确做法是使用 basePayload() 展开。来源:维护者访谈。
适用场景与限制
适用于为库添加 TanStack Devtools 诊断埋点的开发者,尤其是需要跨服务端/客户端、跨标签页观察中间件链、状态转换、生命周期与错误路径的场景。限制:需先掌握 devtools-event-client 技能;生产环境默认不发送事件(除非从 /production 导入或使用 enabled 运行时控制);具体收费、价格、支持平台等信息暂未提供。