performance.measure必须配对mark才能生效,因它仅查表计算两标记时间差,任一缺失则静默失败;常见失效场景包括异步漏打、同名覆盖、大小写不一致、跨域上下文隔离。

performance.measure 为什么必须配对 mark 才能生效
它不自动推导起止点,只做“查表计算”:传入两个标记名,内部去 performance.getEntriesByName() 查找对应时间戳,再相减。任一缺失就静默失败,getEntriesByType('measure') 返回空数组,没有任何报错提示。
常见失效场景包括:
- 异步逻辑中漏打
end标记(如 Promise reject、网络超时、用户中途跳转) - 同名
mark被重复调用覆盖(比如两次performance.mark('click-start'),只有第二次有效) - 大小写或空格不一致(
'ClickStart'和'click-start'视为不同名称) - 在 iframe 或跨域 script 中打点,但未确保上下文一致(
performance是全局对象,但执行环境隔离)
如何用 measure 结果触发视觉反馈补偿
不能等 performance.measure() 调用完才决定是否补偿——它本身不阻塞执行,也不返回耗时值。你得主动查、判断、响应。
典型做法是:在关键交互结束的回调里,立即执行三件事:
- 打
end标记(带唯一 traceId 后缀,避免覆盖) - 调用
performance.measure()构造耗时条目 - 同步调用
performance.getEntriesByName('xxx-duration')拿到最新一条,检查duration是否超阈值(比如 > 300ms),超了就立刻加 loading 动画、禁用按钮、显示 skeleton 等
示例逻辑:
const traceId = Date.now().toString(36);
performance.mark(`search-click-start-${traceId}`);
button.addEventListener('click', () => {
// ... 发起请求
fetch('/search').then(res => {
performance.mark(`search-click-end-${traceId}`);
performance.measure(
`search-click-duration-${traceId}`,
`search-click-start-${traceId}`,
`search-click-end-${traceId}`
);
const entry = performance.getEntriesByName(`search-click-duration-${traceId}`)[0];
if (entry && entry.duration > 300) {
showLoadingIndicator(); // 视觉补偿入口
}
});
});
为什么不能依赖 PerformanceObserver 做实时补偿
PerformanceObserver 的回调是异步微任务,且批量推送——哪怕你刚 measure 完,observer 回调也可能滞后几十毫秒,错过用户第一眼感知窗口。对点击反馈这种亚秒级敏感场景,延迟不可接受。
更关键的是:observer 无法保证顺序。如果用户快速连点两次,两次 measure 可能被合并进一次 observer 回调,你拿到的 entries 数组顺序和实际发生顺序可能不一致,导致补偿逻辑错配。
所以真实项目中,视觉补偿必须走「同步查 + 即时响应」路径,observer 只用于日志上报或离线分析。
清理标记避免内存泄漏和干扰后续测量
每个 mark 会保留在内存中,直到页面卸载或显式清除。大量重复打点(尤其带 traceId 的)会导致 performance.getEntriesByType('mark') 返回几千条,拖慢查询速度,甚至影响 DevTools 性能面板渲染。
建议在补偿逻辑执行后,按前缀批量清理:
- 用
performance.clearMarks('search-click-start-')清除所有匹配前缀的 start 标记 - 同理清理 end 和 measure 条目:
performance.clearMeasures('search-click-duration-') - 不要用
performance.clearMarks()全清——会把导航、资源加载等系统标记也干掉,影响其他监控模块
这点容易被忽略:标记不清,测着测着发现 getEntriesByName 返回结果变多、变慢、甚至查不到新打的点——其实是旧标记占满了缓冲区。

















