性能总览
DirectSurface UI 面向复杂业务和大数据场景。性能优化重点是避免全量扫描、避免无意义布局、只绘制可见内容。
核心原则
- 大数据列表使用虚拟化。
- 滚动时只绘制可见区域。
- hover 不应触发布局。
- 输入时尽量做增量更新。
- 不在 paint 中做昂贵计算。
- 不在 layout 中触发外部业务回调。
- 资源缓存应有生命周期,避免全局泄露。
性能排查的第一步是分层:事件处理、输入会话、布局、绘制、滚动、内存。不要先假设是某个组件慢,应先用诊断工具确认耗时发生在哪条链路。
常见问题
点击卡顿
检查是否在 pointer 事件中做了全量计算,或者同步更新了巨大输入元素。
滚动卡顿
检查是否绘制了不可见节点,或者每次滚动都重建数据结构。
窗口缩放卡顿
检查 layout 是否全量测量所有内容,尤其是大文档、大表格、大树。
内存不释放
检查:
- dispose 是否执行。
- 图片缓存是否挂在文档上下文或组件实例上。
- popup 是否关闭并释放。
- 事件订阅是否解除。
- 大数组是否仍被闭包引用。
排查路径
性能问题不要先猜组件。先判断卡顿发生在哪一层:
| 现象 | 优先检查 | 常见原因 | 主要工具 |
|---|---|---|---|
| 点击后光标延迟出现 | 输入会话、事件处理、rAF 队列 | 隐藏 textarea 保留大文本、pointer 里同步全量计算、焦点同步太重 | Runtime Diagnostics、浏览器 Performance |
| hover 卡顿 | paint request 和 hit test | hover 触发布局、命中测试扫描全量节点、tooltip 反复创建 | Runtime Diagnostics、Layout Inspector |
| 输入卡顿 | 文档模型、layout、paint | 每次输入全量 reflow、中文 composition 重复提交、选区投影扫描全文 | Performance、组件 debugState |
| 滚动卡顿 | 可视范围和绘制数量 | 不可见节点仍绘制、滚动时重建数据结构、滚动穿透导致外层也刷新 | 性能浮层、Runtime Diagnostics |
| resize 卡顿 | layout 层 | 窗口变化时全量测量大文档/大树/大表格 | Layout 性能日志、Performance |
| 内存关闭后不下降 | dispose 和缓存归属 | popup 未关闭、事件订阅未释放、全局缓存持有文档资源 | Heap Snapshot |
指标解释
| 指标 | 含义 | 判断方式 |
|---|---|---|
layoutMs |
当前帧布局耗时 | 持续升高时先看 layout request target 和 stack。 |
paintMs |
当前帧绘制耗时 | 大数据场景应确认可视项数量,而不是总数据量。 |
contentPaintMs |
具体内容绘制耗时 | 如果只在某个组件页面升高,优先查该组件的可视投影。 |
requestCount |
一段时间内刷新请求数量 | 高频请求需要确认是合法动画/光标,还是 setter 循环。 |
dirtyTargets |
本帧脏对象 | hover、光标、选区等应尽量限制在局部对象。 |
快速定位模板
- 先打开 Runtime Diagnostics 或性能浮层。
- 复现问题,不要同时做多个动作。
- 记录是
layout频繁、paint频繁、还是事件处理耗时。 - 如果是
layout,看调用栈是否来自 setter、performLayout()或数据同步。 - 如果是
paint,看可视数量、dirty 范围和是否有不可见节点绘制。 - 如果是输入,检查隐藏输入控件是否保留大 value,composition 是否重复提交。
- 如果是关闭后内存,生成 Heap Snapshot 并按 retained size 找页面、控制器、缓存、闭包。
- 修复后关闭诊断开关,再验证正常交互。
工具
| 工具 | 适合场景 |
|---|---|
| Runtime Diagnostics | 找持续 layout/paint 的 target、reason 和 stack。 |
| Layout Inspector | 查看 render rect、命中区域、对象树和布局异常。 |
| Object Inspector | 展开组件状态、上下文、业务对象和调试数据。 |
| 性能浮层 | 观察当前帧耗时、请求数量和趋势。 |
| 浏览器 Performance | 判断 JS、样式、布局、rAF、事件处理耗时。 |
| Heap Snapshot | 定位关闭页面后仍被引用的对象和缓存。 |
大文档策略
- MarkdownViewer:只绘制可视 block。
- PlainTextEditor:面向几十万行文本,必须虚拟化行。
- MedicalRecordEditor:分页、可视页绘制、增量排版。
什么时候看哪篇文档
| 问题 | 下一步 |
|---|---|
| 不理解 render/layout/paint 请求 | 渲染管线 |
| 布局或窗口缩放卡顿 | 布局性能 |
| 绘制频繁或 paint 时间高 | 绘制性能 |
| GridView 大数据卡顿 | 大数据表格 |
| TreeView 大数据展开卡顿 | 大树性能 |
| PlainTextEditor 大文件卡顿 | 大文本编辑器 |
| 关闭页面后内存不释放 | 内存泄露检查表 |
| 不知道如何使用诊断面板 | 运行时诊断面板 |
修复验收
修复性能问题后至少确认:
- 问题场景下的目标指标下降,例如
layoutMs、paintMs或事件处理耗时。 - 修复没有把耗时转移到滚动、hover、resize 或输入 composition。
- 大数据场景下可视数量与实际绘制数量一致。
- 页面关闭后相关 controller、popup、缓存和大数组不再被持有。
- 临时调试日志和性能开关已关闭或受显式配置控制。