代表性面试主题

如何用 PerformanceObserver 构建可靠的真实用户性能采集?

前端困难
OfferCC 编辑团队发布 更新

题干

请为一个单页应用设计真实用户性能采集方案。要求采集 LCP、资源加载和长任务数据,控制上报成本,并能发现 PerformanceObserver 缓冲区溢出、跨域计时缺失和路由切换带来的重复统计。

1. 题目

一个全球访问的单页应用需要收集真实用户性能数据,用于比较不同设备、网络和版本的体验。你需要采集 LCP、资源加载和长任务,同时限制每个页面会话的上报量。请设计基于 PerformanceObserver 的采集器,并说明它如何处理页面加载前已经产生的条目、缓冲区丢失、跨域资源和 SPA 路由切换。

2. 约束与澄清

  • 先定义每个指标的时间范围:初始文档、软导航,还是一次完整会话。
  • 设定采样率、每类条目上限、批量上报大小和失败重试策略,避免采集器影响页面。
  • 区分浏览器支持差异与数据缺失;缺失值必须带原因,不应默认为零。
  • 明确资源 URL、用户标识和查询维度的隐私边界,避免把完整 URL 或敏感参数直接上报。

3. 核心概念

PerformanceObserver 通过观察指定的 entry type 接收性能条目;buffered: true 可补取观察器建立前已经记录的条目。资源、长任务、绘制和 LCP 等类型有各自的缓冲区限制,回调还可以收到 droppedEntriesCount,用于发现缓冲区已丢弃条目。LCP 是加载期间最大的图像或文本块绘制时间,用户交互后不应继续把后续内容当作初始 LCP。跨域资源若没有合适的 Timing-Allow-Origin 响应头,计时信息可能被限制或缺失。

4. 参考实现

text
startCollector():
  session = createSessionId()
  observe("largest-contentful-paint", {buffered: true})
  observe("resource", {buffered: true})
  observe("longtask", {buffered: true})

observe(type, options):
  if type not in PerformanceObserver.supportedEntryTypes:
    markUnsupported(type)
    return
  observer = new PerformanceObserver((list, _, dropped) =>
    appendSanitized(list.getEntries())
    if dropped > 0: markDropped(type, dropped)
    flushWhenBatchIsReady()
  )
  observer.observe({type, ...options})

onSoftNavigation(route):
  closePreviousView(route)
  resetViewScopedMetrics()

初始化时为每类条目注册独立观察器,优先使用 buffered 补取早期记录;回调只保留需要的字段、裁剪 URL 参数并按批次发送。每个视图保存观察器建立时间、路由和版本,软导航时结束旧视图并重置视图级指标;会话级资源或错误计数则使用明确的去重键继续累计。

5. 数据质量与成本取舍

采样率越高,分位数更稳定,但网络和存储成本也越高。可以对 LCP、长任务保留较高样本,对资源条目采用头部采样或仅保留慢资源。单个 resource 缓冲区默认最多保存 250 条,采集器应在页面生命周期内及时消费并记录丢失数量,而不是无限提高上报频率。客户端批量发送失败时只做有限重试,并在下次页面访问时丢弃过期批次,避免本地队列拖慢启动。

6. 验证与观测

  • 在观察器建立前制造条目,验证 buffered 能补取;在大量资源页面验证 droppedEntriesCount 告警。
  • 用跨域资源分别测试有、无 Timing-Allow-Origin 时的字段差异,并确认缺失原因可区分。
  • 对初始加载、软导航、后台恢复和页面隐藏分别回放,检查 LCP 与资源条目不会跨视图重复归因。
  • 监控采集器自身的 CPU、内存、上报字节数、批次失败率、条目丢失率和支持率。

7. 常见误区

  • 只在 load 事件后创建观察器,导致早期 LCP 或资源条目永远缺失。
  • 看到 droppedEntriesCount 后静默丢弃,却继续把不完整样本当成完整分布。
  • 把跨域资源的受限计时当作真实的零延迟,污染性能分位数。
  • SPA 每次路由都新建观察器却不关闭旧观察器,造成重复上报和内存泄漏。

8. 面试评分点

能正确使用观察器生命周期

应说明 buffered、supportedEntryTypes、观察器关闭与软导航边界,避免早期丢失和重复统计。

能处理缓冲区与跨域限制

应监控 droppedEntriesCount,了解资源缓冲区上限,并用 Timing-Allow-Origin 解释跨域字段缺失。

能设计可控的采样与上报

应按指标价值分配采样率、条目上限和批量策略,说明失败重试不会反过来拖慢页面。

能验证数据可信度

应覆盖早期条目、海量资源、跨域、软导航和后台恢复场景,同时观测采集器自身开销与丢失率。

公开来源

同类题目

资源

产品对比

每份对比均基于官方资料,并标注复核日期。

查看完整对比