一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何通过 performance.mark 在业务关键路径埋点并计算加载耗时

时间:2026-07-26 17:33:49 编辑:袖梨 来源:一聚教程网

performance.mark需配合measure才能准确计时;仅mark不measure则无耗时数据,且mark名须唯一、区分大小写;高频打点需清理,上报应监听measure事件后立即sendBeacon。

performance.mark 不是万能计时器,它只打点、不计算;真正要得出耗时,必须配合 performance.measure 或手动用 performance.getEntriesByName 拿时间戳相减。直接调用 mark 却不 measure,等于埋了点却没读数。

怎么用 mark + measure 正确配对打点

业务中常见错误是只在关键位置调用 performance.mark('start')performance.mark('end'),但漏掉 performance.measure 这一环——结果 getEntriesByType('measure') 里查不到任何数据。

  • 必须显式调用 performance.measure('name', 'start', 'end'),其中两个字符串参数要和 mark 名完全一致(区分大小写、空格)
  • 如果 mark 名重复(比如多次调用 performance.mark('api_start')),measure 默认取最后一次 mark 的时间,容易误判;建议加唯一后缀,如 'api_start_123'
  • 浏览器限制:同一 origin 下最多保留 150 个 performance entry(含 mark/measure/nav 等),高频打点需定期清理:performance.clearMarks()performance.clearMeasures()

为什么不能只靠 getEntriesByName('xxx') 手动算差值

看似可行:const start = performance.getEntriesByName('start')[0]?.startTimeconst end = performance.getEntriesByName('end')[0]?.startTime,再做减法。但实际有坑:

  • 异步场景下,end mark 可能还没执行,getEntriesByName('end') 返回空数组,导致 undefined 参与计算,结果为 NaN
  • 页面卸载前未及时上报,entry 会被清空;而 measure 生成的 entry 更稳定,且支持 entry.duration 直接读取毫秒值
  • getEntriesByName 返回的是数组,需自己处理索引和时序,不如 measure 语义明确、防错性强

如何安全上报 mark/measure 数据到监控服务

不能在 mark 后立刻 sendBeacon,因为 measure 往往滞后;也不宜等页面 unload,可能丢失。推荐在 measure 创建后立即提取并上报:

  • 监听 measure 类型 entry:new PerformanceObserver + observe({ entryTypes: ['measure'] })
  • 过滤出业务相关 measure 名:if (entry.name.startsWith('page_') || entry.name.includes('api_'))
  • navigator.sendBeacon 上报,确保不阻塞主线程:navigator.sendBeacon('/log', JSON.stringify({ type: 'measure', name: entry.name, duration: entry.duration, startTime: entry.startTime }))
  • 注意:sendBeacon 仅支持 POST,且 payload 大小受浏览器限制(通常 ≤64KB),大体积日志需分片或节流

最容易被忽略的一点:mark 的命名必须全局唯一且可追溯。比如 'render_start' 在多个组件里重复使用,后续根本分不清是哪个模块的耗时。建议结合上下文生成命名,例如 `render_start_${componentName}_${timestamp}`,或者统一走业务埋点 SDK 封装,避免裸调 API。

热门栏目