必须用performanceobserver早期注册监听'largest-contentful-paint'、'layout-shift'、'paint'类型获取lcp、cls、fcp,其中fcp需从paint条目筛选name为'first-contentful-paint',且须过滤cls中hadrecentinput为true的条目并累加value。

用 PerformanceObserver 收集真实用户 FCP、LCP、CLS 数据
浏览器原生性能指标必须靠 PerformanceObserver 捕获,不是靠 performance.getEntriesByType() 一次性读取——后者只返回已结束的条目,漏掉页面生命周期中持续变化的关键指标(比如动态加载内容触发的 LCP 延后)。
实操要点:
- 监听类型必须设为
'largest-contentful-paint'、'layout-shift'、'first-contentful-paint',不能写错大小写或拼写 - 需在页面最顶部立即注册 observer,否则可能错过首屏关键指标(尤其 FCP 常在 300ms 内触发)
- 对
layout-shift要过滤掉entry.hadRecentInput === true的条目,避免把用户滚动/点击导致的位移计入 CLS - 每个条目只上报一次,用
entry.name或entry.startTime去重,防止重复采集
用 Chart.js 渲染带时间轴的多指标折线图
直接用 canvas 手绘趋势图成本高、响应式难做;Chart.js 是目前最轻量且兼容性稳妥的选择(v4+ 支持 time 类型 x 轴,无需手动转时间戳)。
关键配置注意点:
- x 轴类型设为
'time',time.unit推荐用'second'(不是'millisecond'),否则图表密度过高、标签挤成一团 - 多个指标共用同一时间轴时,确保所有数据点的
t字段都是Date实例或 ISO 字符串(如'2024-06-15T14:22:08.123Z'),不能混用毫秒数值 - FCP/LCP 单位是毫秒,但图表 y 轴建议统一转成秒并保留 1 位小数(
value / 1000),更符合人眼读数习惯 - CLS 是无量纲值,范围通常在 0–0.3 之间,y 轴 max 设为
0.35,避免被其他指标拉伸失真
前端聚合逻辑:按分钟分桶 + 滑动窗口去噪
原始性能数据太稀疏(单页仅几个点)或太密集(滚动触发多次 LCP),直接画图会断续或毛刺严重。必须在前端做轻量聚合,不依赖后端预计算。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
推荐做法:
- 用
Map存每分钟的桶(key 为Math.floor(Date.now() / 60e3)),每个桶内存该分钟所有指标的数组 - 每分钟结束时,对每个桶计算中位数(非平均值)——更能抵抗偶发卡顿、调试工具干扰等离群值
- 维护一个长度为 60 的滑动窗口(最近 1 小时),超出则
delete最老桶,避免内存泄漏 - 图表更新时,只传入窗口内已聚合的
{x: Date, y: number}数组,不传原始事件流
为什么不用 Web Vitals 库直接上报?
web-vitals 库确实封装了 FID、CLS 等计算逻辑,但它默认只做「单次上报」,且不提供时间维度聚合能力。你要画趋势图,就得自己接管采集时机、去重规则和时间分桶——库只是帮你算单个值,不是帮你建时序数据管道。
容易踩的坑:
- 直接调
getFID()或getCLS()并塞进图表,结果发现只有 1–2 个点,因为这些函数只在指标「确定结束」时才回调(比如 FID 等到用户首次交互) - 误以为
onFCP()会每秒触发,实际它只触发一次,且可能因资源加载延迟到 DOMContentLoaded 之后 - 没处理
attribution字段,在 SPA 路由切换后,LCP 可能指向旧路由的图片,导致趋势图出现不合理跳变
趋势图真正的难点不在画线,而在让每个点都代表「可比、稳定、可归因」的真实体验片段。时间戳对齐、指标生命周期理解、前端聚合精度,三者缺一不可。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










