前端用 PerformanceObserver 采集核心指标的结构
前端采集 LCP、CLS、INP 时,应把 PerformanceObserver 当作性能条目的观察入口,而不是现成的指标计算器。可维护的结构是:观察原始条目、按指标规则计算、在合适的页面生命周期时机产生快照,再交给独立上报层。正式核心指标建议复用 web-vitals 的计算逻辑,原生 Observer 留作受控诊断。
web-vitals 官方项目地址:https://github.com/GoogleChrome/web-vitals。Performance Timeline 规范地址:https://www.w3.org/TR/performance-timeline/。下面是采集端接入手册,不代表本页已完成性能测量,也不提供虚构的用户分数。
指标突然变好,先确认不是采集断了
上线后 LCP、CLS 或 INP 样本骤减,不应立即得出体验改善的结论。先比较脚本是否成功加载、浏览器是否支持所需条目、指标回调是否出现、上报是否进入队列。缺失和零值必须区分:没有可测交互的访问,不应被补成 INP=0 来拉低统计结果。
| 指标 | 原始观察方向 | 容易写错的捷径 |
|---|---|---|
| LCP | largest-contentful-paint 候选条目 | 只取首个候选,或把回调执行时间当成值 |
| CLS | layout-shift 条目 | 累加整次访问的所有偏移 |
| INP | event 等交互时序信息 | 把每个事件 duration 直接取最大值 |
LCP 关注主要内容呈现候选;CLS 采用最大会话窗口的累积偏移得分,并排除符合规则的近期用户输入相关偏移;INP 依据逻辑交互而不是孤立事件计算。不要把三种条目统一套进“取最后一个 duration”的函数。
快速判断:只观察浏览器实际支持的类型
以下诊断模块只计数并打印支持情况,不计算正式指标、不上传条目,也不采集 DOM 或地址。它可独立放在测试环境中,不应与正式指标共用一个没有语义标签的数值字段。
// 原始条目诊断,不是 LCP、CLS、INP 的计算实现
const observers = [];
const rawCounts = Object.create(null);
const supported = typeof PerformanceObserver === "undefined"
? []
: PerformanceObserver.supportedEntryTypes || [];
for (const type of ["largest-contentful-paint", "layout-shift", "event"]) {
if (!supported.includes(type)) {
console.info("entry type unavailable:", type);
continue;
}
try {
const observer = new PerformanceObserver((list) => {
// 只统计原始条目数量,不把数量或 duration 冒充核心指标
rawCounts[type] = (rawCounts[type] || 0) + list.getEntries().length;
});
observer.observe({type, buffered: true});
observers.push(observer);
} catch (error) {
// 单个类型失败不应让业务页面初始化失败
console.warn("observer unavailable:", type, error.name);
}
}
function stopRawDiagnostics() {
// 仅供结束本段诊断使用,不是正式指标的最终结算
for (const observer of observers) observer.disconnect();
}
buffered 用来请求该类型先前缓冲的可用条目,不等于无限历史重放,也不保证每种类型在每个浏览器都存在。event 条目数量受事件时序的采集条件影响,不能由计数推算用户交互总数。Observer 接口依据 Performance Timeline 规范。

处理步骤:让计算层输出指标快照
在已通过包管理器安装并由构建工具解析 web-vitals 的浏览器模块中,可以用下面的接入骨架。/telemetry/vitals 是示例同源接收路径,必须由项目自行实现;这段代码不会替你创建服务端接口。每个文档只初始化一次,避免组件挂载或路由切换时反复注册。
import {onLCP, onCLS, onINP} from "web-vitals";
// 自定义上报开关与序号,不属于 web-vitals 的指标算法
let reportingEnabled = true;
const revisions = new Map();
function reportSnapshot(metric) {
if (!reportingEnabled) return;
const key = metric.name + ":" + metric.id;
const revision = (revisions.get(key) || 0) + 1;
revisions.set(key, revision);
// 只保留指标字段;不上传 entries、完整URL、DOM或用户输入
const payload = {
schema: 1,
id: metric.id,
name: metric.name,
value: metric.value,
revision,
navigationType: metric.navigationType
};
try {
const body = JSON.stringify(payload);
const queued = typeof navigator.sendBeacon === "function"
&& navigator.sendBeacon("/telemetry/vitals", body);
if (!queued) {
// 入队失败只记录状态,不在页面退出时无限重试
console.warn("vitals report not queued");
}
} catch (error) {
// 遥测异常不能打断业务交互
console.warn("vitals transport unavailable:", error.name);
}
}
// 由库处理指标自身的计算和生命周期回调;不在原始条目回调里重写算法
onLCP(reportSnapshot);
onCLS(reportSnapshot);
onINP(reportSnapshot);
export function disableVitalsReporting() {
// 关闭传输,不声称解除库已经注册的内部观察器
reportingEnabled = false;
}
接收端需能按文本请求体解析 JSON。建议按 name 与 id 识别同一指标实例,并保留较大的 revision;这里发送的是 value 快照,不是可直接相加的增量。网络到达顺序未必和回调顺序相同,重复回调不能作为多次独立页面访问累计。
同一文档可能经历隐藏、恢复或后退前进缓存恢复,不要假定指标回调整次只出现一次。库接入约定见 web-vitals 官方用法;所用依赖版本应在项目中固定并随升级维护,本文不把源码主分支等同于所有历史版本。
上报时机:不要等到 unload 才想起采集
LCP 的候选会随页面呈现变化;CLS、INP 还可能随页面使用继续更新。只在 window.load 读取一次,会遗漏后面的行为。页面转为 hidden 是重要的结算和发送机会,但隐藏不是永久关闭:用户返回页面后,仍可能有新的指标快照。
上面的骨架在指标回调内直接入队发送,让库处理相应的隐藏时机,而不是另建一个可能先于指标更新执行的“最终值”缓存。若改为业务自己的批量队列,需要单独处理生命周期顺序和队列上限,不应只给 unload 加一个监听器就宣称数据完整。

Beacon 规范明确区分入队与实际传输。sendBeacon 返回 true 不是服务端确认,没有响应回调,也不能作为投递成功率的分母。这个最小骨架不包含可靠离线队列、重试、采样或服务端鉴权方案。
回退路径:先关闭传输,不改变指标定义
若上报脚本造成负担,可先关闭传输开关,回退遥测模块版本;不要为了让错误率下降,把未支持的指标补成零、去掉关键计算规则或让异常向业务层冒泡。诊断 Observer 可以结束,但库内部生命周期不能用上面的自定义开关假装已经销毁。
复核时记录三个独立状态:条目类型不支持、指标尚未形成、已有快照但未能入队。后端没有数据时,还应检查接收路径和请求体处理。只改前端公式可能掩盖真正的传输故障。
确认告警恢复前,先看样本是否可比较
将指标值、样本覆盖率和重复更新处理分开观察。LCP、INP 通常以毫秒表示,CLS 是无单位得分,不能放在同一个时长直方图中。页面类型、访问方式与浏览器分布不同,也不应被粗暴混成一个平均值。
复盘应保留模块版本、支持情况、初始化次数、接收端去重键与字段版本。本文没有收集任何真实指标,也不声称这一骨架与浏览器外部统计平台在所有情形下完全一致。
为什么没有交互时收不到 INP?
缺少符合条件的交互时,不应凭空造一个数值。采集覆盖与交互是否发生要独立解释,不能把“没有测量值”当成“响应非常快”。
为什么不上报整个 entry 对象?
核心指标上报通常只需要受控字段。完整条目可能带来无关上下文、隐私风险和体积成本;需要诊断归因时,应另定字段白名单,而不是无差别序列化。
能把每个 SPA 路由都直接当成新页面吗?
不能仅凭路由变化重置这些指标。软导航需要明确的测量口径与对应支持;本文处理文档生命周期,不把普通路由监听伪装成标准新导航。
前端键盘焦点样式与 :focus-visible 的应用
- 上一篇
- 前端键盘焦点样式与 :focus-visible 的应用
- 下一篇
- Vite 环境变量暴露边界与构建替换
-
- 文章 · 前端 | 1小时前 | 前端 · typescript ·
- TypeScript 自定义类型守卫收窄 API 响应
- 493浏览 收藏
-
- 文章 · 前端 | 1小时前 | 浏览器缓存 首屏加载 依赖图 JavaScript性能 ES module modulepreload
- ES module preload 减少首屏依赖等待的方法
- 254浏览 收藏
-
- 文章 · 前端 | 1小时前 | 环境变量 · javascript · 前端工程 · 敏感信息 前端构建 import.meta.env Vite环境变量 VITE_
- Vite 环境变量暴露边界与构建替换
- 256浏览 收藏
-
- 文章 · 前端 | 1小时前 | 前端 · css · 无障碍 :focus 焦点样式 :focus-visible 前端键盘焦点
- 前端键盘焦点样式与 :focus-visible 的应用
- 433浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- ARIA dialog 实现可访问弹窗的焦点管理
- 411浏览 收藏
-
- 文章 · 前端 | 1小时前 | css · 行高 CSS flex align-items inline-flex Baseline 文字图标对齐
- CSS Flex baseline 对齐文字和图标的做法
- 474浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- CSS Grid minmax 处理响应式列宽的方案
- 422浏览 收藏
-
- 文章 · 前端 | 1小时前 | 前端 · css · 卡片布局 换行 CSS text-wrap balance 多行标题 前端排版
- CSS text-wrap: balance 怎样改善多行标题换行
- 439浏览 收藏
-
- 文章 · 前端 | 1小时前 | 前端 · 性能优化 · javascript · ArrayBuffer postMessage 前端性能 结构化克隆 Web Worker transfer terminate
- Web Worker 分担大数据计算的消息设计
- 176浏览 收藏
-
- 文章 · 前端 | 1小时前 | 版本发布 · Service Worker Cache API 缓存版本 前端缓存 activate Service Worker更新
- Service Worker 缓存策略的版本更新方法
- 122浏览 收藏
-
- 文章 · 前端 | 1小时前 | 前端 · css · 可访问性 · 浏览器兼容 · prefers-reduced-motion animation-timeline 前端兼容 CSS scroll-timeline 滚动驱动动画
- CSS scroll-timeline 结合 prefers-reduced-motion 的降级处理
- 308浏览 收藏
-
- 文章 · 前端 | 2小时前 | html · javascript · 表单 · 浏览器API · 自定义按钮 前端表单 HTMLInputElement.showPicker input选择器
- HTMLInputElement.showPicker 怎样用自定义按钮打开选择器
- 106浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 500次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 558次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 587次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 539次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 349次使用
-
- JavaScript函数定义及示例详解
- 2025-05-11 502浏览
-
- 智能体安全引领产业升级——国内AI安全产品市场深度分析
- 2026-08-21 501浏览
-
- CSS变量简化按钮悬停效果技巧
- 2026-05-31 501浏览
-
- JavaScript符号类型详解与应用
- 2026-05-31 501浏览
-
- HTML剪贴板复制粘贴怎么用
- 2026-05-26 501浏览
