用 performance.mark + performance.measure 是建立可复现、可比对、可嵌入自动化流程的业务性能基准最轻量且标准的方式;它写入标准 PerformanceEntry,被所有现代调试工具原生支持,而 console.time 缺乏跨上下文稳定性、不可导出、无法与原生事件对齐。
直接结论:用 performance.mark 打点 + performance.measure 定界,是建立可复现、可比对、可嵌入自动化流程的业务性能基准最轻量且标准的方式。
console.time 的标记不具备跨上下文、跨异步链路的稳定性;它无法被 DevTools Timeline 或 ndb 自动识别,也不能导出为结构化指标。更重要的是,它不参与浏览器 Performance Timeline 的统一时序系统——这意味着你无法把它和 navigationStart、domContentLoadedEventEnd 等原生事件对齐分析。
而 performance.mark 写入的是标准 PerformanceEntry,所有现代调试工具(Chrome DevTools、ndb、Lighthouse、甚至 RUM SDK)都原生支持解析和聚合这些标记。
不是所有函数调用都值得标记。真正构成性能基线的 mark,必须满足三个条件:
'checkout_start'、'search_submit'、'payment_init',而不是 'render_loop_1' 这类技术性描述requestAnimationFrame 里每帧都 mark),否则会污染 Timeline 并拖慢运行时示例(真实可用):
performance.mark('search_submit');fetch('/api/search', { method: 'POST', body: JSON.stringify(q) }) .then(r => r.json()) .then(data => { performance.mark('search_response_parsed'); performance.measure('search_total', 'search_submit', 'search_response_parsed'); renderResults(data); });
打完 mark 和 measure 只是第一步。要形成可追踪的基准,必须把结果导出并持久化:
performance.getEntriesByName('search_total', 'measure') 拿到耗时(单位毫秒),不要依赖控制台打印performance.getEntriesByType('navigation') 获取当前页面加载上下文,便于归因(比如区分首次加载 vs 后续搜索){ name: 'search_total', duration: 246.3, env: 'prod', version: 'v2.4.1' },否则无法做版本间对比performance.clearMarks() / clearMeasures() 避免内存累积(尤其在单页应用中反复操作时)看似简单,但实际落地常踩几个隐性坑:
performance.mark 在 IE 中完全不可用,Safari 13.1 之前不支持 detail 字段传参,如需携带额外元数据(如用户 ID、AB 实验分组),得降级用自定义属性拼接字符串performance.now()),但某些嵌入式 WebView 或旧版 Electron 可能只到毫秒级,导致 measure 结果出现 1–2ms 跳变postMessage 传递时间戳,再用 performance.timeOrigin 对齐基准search_total 耗时,和你自己代码里 getEntriesByName 拿到的值可能差几毫秒,因为工具采集存在采样延迟,别拿它当绝对真值校验,只用于趋势判断真正难的不是打点,而是让每个 mark 都对应一个可解释、可归因、可随业务演进持续维护的语义单元。一旦标记开始漂移(比如 'pay_click' 实际包含了支付 SDK 初始化),基准就失效了。