10|性能诊断
第七章 / 10。目标:先形成可证伪假设,再选择工具和优化点。
从用户动作建立诊断链
以“搜索框输入后结果迟到”为例,先固定路由、数据、生产构建、浏览器版本和设备条件,再记录:
text
输入事件
→ handler / 状态写入
→ Vue 调度与组件 render
→ patch / DOM 提交
→ style / layout / paint
→ 下一帧呈现网络慢、JS 长任务、组件更新面过大、布局抖动会落在不同阶段。只看 Vue DevTools 不能证明浏览器布局快;只看 Network 也不能解释响应返回后的长任务。
最小测量包装器
ts
export async function measureAction<T>(name: string, action: () => Promise<T> | T) {
const start = `${name}:start`
const end = `${name}:end`
performance.mark(start, { detail: { route: location.pathname } })
try {
return await action()
} finally {
performance.mark(end)
performance.measure(name, start, end)
performance.clearMarks(start)
performance.clearMarks(end)
}
}调用方必须决定 action 的终点:只等请求、等 nextTick,还是等下一帧,三者回答不同问题。User Timing 的 duration 是高精度时间差,不是“Vue 耗时”的自动归因。
开发环境可设置 app.config.performance = true,在支持的浏览器 Performance 面板看到 Vue 初始化、render、patch 等标记;Vue DevTools profiler 用于找更新频繁或耗时组件。最终结论仍应回到生产构建,因为开发警告、热更新和 source map 会改变成本。
同名动作在这个最小包装器中应串行测量:并发调用会复用同名 mark,互相清理后得到错误配对。要测并发请求,给每次调用分配唯一 id,再把公共 operation 名放进 detail;独立命名与时间单位都是测量合同的一部分。
指标与问题必须匹配
| 用户问题 | 证据组合 | 容易误判 |
|---|---|---|
| 首屏主要内容迟 | LCP、网络瀑布、关键资源、服务器时间 | 只看 bundle 总大小 |
| 点击后迟迟不更新 | INP/事件时间、长任务、组件与布局时间线 | 用平均 FPS 代替交互延迟 |
| 滚动掉帧 | 帧时间、脚本、布局、绘制、DOM 数量 | 只统计 render 次数 |
| 越用越慢 | heap 快照、listener/计时器、缓存实例数 | 一次刷新后的内存值 |
优化比较要先证明行为等价,保持输入和采样方法一致,并报告中位数/分位数或完整样本。一次更快可能只是缓存、JIT、后台任务或测量噪声;不要写固定收益承诺。
一条可执行排查顺序
- 录制一次慢交互,标出用户动作与下一帧。
- 判断主要时间在网络、脚本、布局还是绘制。
- 若在 Vue 更新,检查哪些组件更新、props 是否稳定、列表节点数和 watcher 链。
- 只改一个边界,再跑行为测试与同场景多轮测量。
- 收益不稳定或维护成本更高时撤销优化,保留诊断证据。
练习:写可证伪的性能报告
对一个 1,000 行可筛选列表记录至少 7 次同一输入操作。报告必须包含环境、数据生成方式、操作步骤、原始样本、DOM 节点数、假设、单一改动和行为等价断言;不得只给百分比。若优化后分布重叠,结论应是“当前证据不足”。
参考答案:第 7 章第 10 题。