09-性能与调试:Binding 循环、过度创建、Profiler/Scene Graph¶
目录页:见 QML 教程目录
目标¶
- 能识别最常见的 QML 性能问题与“看起来像 bug 的性能抖动”
- 会排查 binding loop、过度创建 Item、列表卡顿
- 知道 Qt Creator / QML Profiler 的常用观察点(思路为主)
约束:本教程示例不使用 QtQuick.Controls。
1. Binding loop:最常见也最隐蔽¶
典型症状:
- 控制台出现 “Binding loop detected”
- UI 轻微抖动/尺寸跳动
- 某个属性值不稳定(每帧都在变)
常见原因:
- A 依赖 B,B 又依赖 A(直接或间接)
- 在
onWidthChanged里又去改width(事件里写回同属性)
一个容易踩的例子(不要这么写):
更稳的写法:
- 用纯绑定表达式描述关系
- 或者把“输入状态”和“UI 表现属性”分离(状态驱动,见第 04 章)
2. 过度创建 Item:看似简单,实际很重¶
QML 性能最容易被拖垮的点之一是:
- 一次性创建了太多 Item(尤其是带 Text/Image/复杂动画的)
建议:
- 小数量重复 UI:
Repeater没问题 - 大列表:优先
ListView(delegate 复用) - 可见范围之外的内容:用
Loader按需加载
你可以把直觉记成一句话:
- “让屏幕上可见的东西尽量少创建,屏幕外的东西尽量不创建。”
3. ListView 卡顿:优先排这几类原因¶
3.1 delegate 太重¶
症状:
- 滑动掉帧
- 首次进入列表页卡一下
处理:
- delegate 里避免深层嵌套
- 少用复杂阴影、复杂路径绘制(能不用就不用)
- 动画尽量只做 opacity/transform(通常更便宜)
3.2 图片加载策略不当¶
处理:
- 大图尽量异步:
Image { asynchronous: true } - 避免每个 delegate 都加载大图且每帧缩放
3.3 绑定表达式过重¶
处理:
- 把复杂计算从绑定里抽出去(必要时用中间 property 缓存结果)
- 避免在绑定里做循环/字符串大量拼接
4. 动画与渲染:不要“每帧改布局”¶
原则:
- 尽量动画
opacity/scale/rotation/x/y(通常更友好) - 尽量少动画会触发布局重算的属性(例如复杂层级下的
width/height)
并不是说不能动画 width/height,而是:
- 动画它们时,更要控制层级、避免联动太多绑定
5. 调试方法(思路清单)¶
当你遇到“卡/抖/不稳定”时,可以按顺序做:
- 先看控制台:有没有 binding loop、类型找不到、加载失败
- 先缩小范围:把可疑区域用
visible: false或Loader.active: false隔离 - 把重 UI 拆开:列表 delegate 先删到只剩 Rectangle,看卡顿是否消失
- 确认是否过度创建:是否一次性创建了大量子项(Repeater + 大模型)
- 用 Profiler 定位:如果你在 Qt Creator,用 QML Profiler 看创建/绑定/动画的热点
6. Scene Graph 常识(够用级)¶
你不需要先精通 Scene Graph,但建议记住两点:
- “能合批渲染”的场景更快:过多不同材质/复杂效果会降低合批
- “减少层级与变化源”通常就能明显提升流畅度
常见坑¶
- 把调试逻辑写进绑定:绑定会频繁求值,容易造成额外开销
- 在 onXChanged 里疯狂 setProperty:容易引入 loop 与抖动
- 用 Repeater 做几千条列表:应该换 ListView
小结¶
- 优先修掉 binding loop,这是很多“玄学问题”的根
- 大数据列表用 ListView,按需加载用 Loader
- 动画优先 opacity/transform,减少每帧触发布局重算
下一篇:见 10-QML常用元素与属性速查