只打 mark 看不到耗时,因为 mark 仅记录时间戳,不生成区间信息;必须调用 measure 才能基于两个 mark 计算并创建含 duration 的 measure 条目。

单独调用 performance.mark() 不会产生耗时数据,必须搭配 performance.measure() 才能拿到可上报、可分析的毫秒级差值。
为什么只打 mark 看不到耗时?
浏览器不会自动把两个同名或语义相关的 mark 关联起来——performance.mark('api-start') 和 performance.mark('api-end') 只是各自存一条时间戳,不生成任何“区间”信息。只有显式调用 performance.measure(),浏览器才创建一条 entryType: 'measure' 的性能条目,含 duration 字段。
常见现象:DevTools 的 Performance 面板里能看到标记点,但在 Timings 或 Network 标签页查不到对应耗时条目;performance.getEntriesByType('measure') 返回空数组。
-
performance.measure()是纯计算操作,不记录新时间点,只取已存在的 mark 时间戳做减法 - 若任一 mark 名不存在(比如异步未执行完就跳转),
measure静默失败,生成的条目duration为0,且不会出现在getEntriesByType('measure')结果中 - mark 名区分大小写,且不能含空格或特殊字符;推荐用小写+短横线,如
'payment-submit-start'
measure 调用时机和参数配对要点
performance.measure() 必须在两个 mark 都已执行后调用,且字符串参数要完全一致(包括大小写、拼写、有无空格)。它不关心逻辑是否在同一个函数里,只认名字。
立即学习“前端免费学习笔记(深入)”;
- 起点 mark 必须在
measure之前执行,终点 mark 必须在之后(或同一微任务末尾) - 重复使用相同 mark 名会覆盖前一次时间戳;若流程可能复用(如组件多次挂载),建议加轻量唯一标识,例如
`api-fetch-start-${Date.now()}` - 可以省略起始名:
performance.measure('load-time', 'loadEventEnd')默认以navigationStart为起点 - 不能传时间戳数值(如
performance.timing.loadEventEnd),measure()只接受字符串名
如何安全提取并上报 measure 结果?
performance.measure() 创建后,数据不会自动暴露,也不主动触发上报。你得自己拉取、过滤、发送,且要防空、防重、防堆积。
- 立即读取可用
performance.getEntriesByName('xxx-duration', 'measure'),但返回的是数组,需判空再取[0]?.duration - 更稳妥的做法是批量捞:
performance.getEntriesByType('measure'),再用.filter(e => e.name.startsWith('api-'))提取业务相关项 - 重复触发同一业务流程会导致历史数据堆积;上报前建议用
performance.clearMeasures()清理,或只取最近一条:.slice(-1) - 生产环境推荐用
PerformanceObserver监听,但必须显式配置{ entryTypes: ['measure'] },否则回调永不触发
SPA 中容易踩的三个坑
在 React/Vue 等框架里,mark 时间点极易“漂移”,导致测的不是用户感知耗时,而是技术阶段耗时。
- 不要在
setState或mounted后立刻打 end mark——DOM 更新还没完成;应等useEffect、nextTick或requestIdleCallback再打 - 用户点击跳转后,原页面的 mark 会被丢弃;若要跨页测量(如点击→详情页首图加载),需改用
sessionStorage透传起点时间,或改用导航 Timing API(如navigationStart) - iOS Safari 对
mark/measure的 flush 有延迟;不要依赖getEntriesByName立即返回,优先用getEntriesByType('measure')+ 过滤
真正难的不是写对那两行 mark 和 measure,而是判断“用户到底什么时候才算开始、什么时候才算结束”。这个判断一旦偏了,后面所有数字都失去业务意义。


















