DirectSurface UIDirectSurface UI
开始使用
文档/业务指南

运行时诊断

import { RenderRuntimeDiagnosticsPanel, type AppHost } from 'ds-ui'

运行时诊断用于排查 DirectSurface UI 的布局、绘制、重绘请求、焦点、弹窗、窗口和性能问题。它面向复杂业务应用开发者和现场问题复现人员。

这篇是操作手册:重点说明如何打开工具、如何看指标、如何根据现象定位到代码。

诊断工具链

工具 用途 适合问题
性能浮层 看当前帧耗时、layout/paint 趋势和是否持续刷新。 页面卡顿、持续重绘、输入延迟。
DevTools Runtime tab 看 paint/layout request 历史、热点聚合、调用栈和 dirty target。 “谁一直请求刷新”。
DevTools Layout tab 看 render tree、节点 rect、命中区域、app context 和选中对象状态。 布局错位、命中异常、上下文读错。
ObjectInspector 展开对象、复制值、查看 debugState 和上下文值。 状态异常、对象结构不清楚。
浏览器 Performance 看 JS、rAF、事件处理、样式和浏览器主线程耗时。 框架日志很快但体感仍卡。
Heap Snapshot 看关闭页面后对象是否仍被引用。 内存泄露、大文档关闭后内存不降。

打开入口

demo 中通常通过顶部菜单的“调试”打开性能浮层、paint request debug、DevTools 和诊断快照。业务应用可以用自己的菜单或快捷键接入这些能力。

推荐通过 AppOverlayService 打开 DevTools:

function openDiagnostics(host: AppHost): void {  const uiServices = host.uiServices   uiServices.showDevTools({ activeTab: 'layout-inspector' })  uiServices.showDevTools({ activeTab: 'runtime' })}

如果直接组合诊断面板,最小结构如下:

const panel = new RenderRuntimeDiagnosticsPanel({  state: {    paintRequestDebugEnabled: true,    paintRequests: [],  },})

真实应用中,state 应来自运行时 host 或应用服务,不应长期手工维护空数组。

标准排查流程

  1. 打开性能浮层,确认是 layout、paint、输入事件还是浏览器主线程问题。
  2. 打开 paint request debug。
  3. 打开 Runtime Diagnostics Panel。
  4. 复现问题,只做一个动作,例如点击一次、输入一个字、滚动一次。
  5. 查看 request 列表,确认是否持续增长。
  6. 切到 hotspots,看最高频 target、kind、source 和 reason。
  7. 点开详情,用 ObjectInspector 查看 stack、dirty layers、layoutTargets、paintTargets。
  8. 回到对应组件,检查 setter、layout、paint、事件处理和数据同步。
  9. 修复后关闭 debug,再用正常交互验证。

判断问题在哪一层

现象 优先看 判断依据
点击后光标迟迟不出现 Performance + 输入组件 debugState pointer 事件很快但 rAF 晚,可能是隐藏输入、浏览器主线程或焦点同步。
hover 卡顿 Runtime Diagnostics hotspots hover 是否触发 layout,命中测试是否扫描全量树。
输入中文卡顿 输入事件、composition、layout 日志 composition update 是否重复排版,hidden textarea 是否保留大 value。
页面空闲仍高频绘制 paint request 列表 是否有光标、动画、sidebar、tabs 等组件反复 markNeedsPaint
页面空闲仍高频 layout layout request stack 是否在 performLayout() 中反复 setItems() 或写同样状态。
滚动卡顿 可视数量和 paint 耗时 是否绘制不可见节点,滚动容器是否嵌套。
resize 卡顿 layout targets 是否窗口缩放时全量测量大文档、大树、大表格。
关闭后内存不降 Heap Snapshot 页面 root、controller、context、popup、cache 是否仍被引用。

Runtime Diagnostics Panel 怎么看

Requests 视图

Requests 视图按时间列出刷新请求。重点看:

  • seq:请求顺序。
  • kindframelayout
  • source:来自 pipeline、overlay、runtime、window 还是 debug。
  • target:发起请求的 RenderObject。
  • reason:请求原因。
  • alreadyScheduled:当时是否已有帧被调度。
  • layoutNeeded:是否已经存在 layout 需求。

如果同一个 target 在空闲状态下不断出现,优先查它的 setter、定时器、动画或 layout 同步逻辑。

Hotspots 视图

Hotspots 会按 target/kind/source/reason 聚合。它适合回答:

  • 哪个组件请求最多。
  • 是 layout 多还是 paint 多。
  • 是同一个 reason 反复触发,还是多个来源混杂。
  • 是否存在已经 scheduled 仍持续请求的情况。

修复高频刷新时,先处理 count 最高且业务上不应持续变化的项。

详情面板

详情面板由 ObjectInspector 组成,可以展开:

  • request 原始记录。
  • stack。
  • dirty layers。
  • pipeline 状态。
  • layout targets。
  • paint targets。
  • transient paint targets。

stack 最有价值。它能直接告诉你是哪一行代码触发了 markNeedsLayout()markNeedsPaint()

常见案例

layout 中反复 setItems

现象:

RenderReviewSidebar.setItems
RenderMedicalRecordEditor.syncReviewSidebarLayout
RenderMedicalRecordEditor.performLayout

判断:layout 阶段每次都给子组件写入 items,即使内容没变,也会触发子组件继续 layout。

修复方向:

  • 给 items 做签名比较。
  • 内容没变时不调用 setter。
  • 避免在 performLayout() 中构造新数组并写入子组件。

hover 触发布局

现象:鼠标移动时 Runtime Diagnostics 不断出现 kind: layout

判断:hover 应只影响视觉状态,通常只需要 markNeedsPaint()

修复方向:

  • hover setter 只改绘制状态。
  • 不在 hover 时重建模板或子树,除非组件明确需要。
  • 需要重建模板时,限制在当前可视项。

光标闪烁导致整页重绘

现象:存在输入光标时持续高频 paint,且 target 是页面根或大容器。

判断:光标闪烁应尽量是局部绘制,不应让大文档整页重绘。

修复方向:

  • 光标只请求局部 dirty rect。
  • 不因光标闪烁触发 layout。
  • 检查父组件是否把局部 paint 放大成整页 dirty。

大对象查看器展开卡顿

现象:打开诊断面板或 ObjectInspector 后点击展开大对象卡顿。

判断:一次性枚举太多属性或复制太大值。

修复方向:

  • 降低 maxProperties
  • 设置 propertyGroupSize
  • 设置 expansionBatchSize
  • 对业务敏感或超大字段先脱敏或摘要化。

诊断快照

诊断快照用于离线分析。它适合现场复现后导出,再给开发者分析。

快照应包含:

  • 运行时状态。
  • 最近 paint/layout request。
  • 性能 samples。
  • render tree 摘要。
  • window/popup 状态。
  • app context 摘要。

快照不应包含未脱敏的患者信息、账号 token、完整病历内容或大体积业务数据。

诊断结束后的检查

修复后至少确认:

  • 性能浮层不再显示异常高频 layout/paint。
  • Runtime Diagnostics hotspots 中目标 count 不再持续增长。
  • 关闭 debug 后正常交互仍然流畅。
  • 相关组件的 debugState() 不再显示异常状态。
  • 如果是内存问题,关闭页面后重新生成 Heap Snapshot 验证 retained object。

常见错误

长期开启无限请求历史

诊断工具本身会持有 request 和 stack。只在复现问题时打开,结束后清空或关闭。

只看 paintMs,不看 paint request 来源

单次 paint 很快也可能体感卡,因为请求频率过高。需要同时看频率、来源和浏览器主线程。

把诊断输出当业务审计

诊断数据服务开发和排障,不是业务审计日志。业务审计应在命令、服务或业务流程层记录。

没有给复杂组件提供 debugState

复杂组件应提供 debugState(),让诊断面板和 Layout Inspector 能看到内部状态,例如 visible row count、selected key、scroll offset、painted page count。

相关文档