尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

前端数据埋点(6):web-vitals——三个数怎么算出来

前端数据埋点(6):web-vitals——三个数怎么算出来 上一篇框架钩子——Vue 接住的错window.onerror 听不到下一篇Web Vitals——页面慢不慢不是再包一层 fetch相关一次点击如何变成一条埋点 · 上报系列前端数据埋点· 第 6 篇前五篇是错误、点击、路由、信封。本篇加餐从库讲起页面体感慢不慢听的是 Performance 时间线不是再包一层fetch。web-vitals 把时间线收成 LCP / INP / CLS。讲机制不是把官方 README 抄一遍。源码按 v5.1.0。何时装信封、切标签和后退缓存下一篇再写。先说结论结算页能打开按钮也能点用户仍可能觉得卡。这不是TypeError也不是pay_click点了几次是三个数指标人的体感单位LCP最大那块内容何时画完首屏主图、大标题毫秒INP点下去到画面有反馈最差的那一次毫秒CLS排版跳了多少按钮突然被顶下去无量纲分数自己new PerformanceObserver去听largest-contentful-paint很容易漏预渲染要从激活时刻起算、页已经 hidden 之后的绘制不要记、用户一点击 LCP 就该停。INP、CLS 各自还有另一堆规则。web-vitals收进三个函数。你只负责订阅。三个回调拿到的都是Metric顶层字段一样「是哪个节点」在entries里没有metric.element。import{onLCP,onINP,onCLS}fromweb-vitalsonLCP((metric){constentrymetric.entries[metric.entries.length-1]// metric.name LCP// metric.value 毫秒比如 2400// metric.rating good | needs-improvement | poor// metric.id 这一页这次 LCP 的实例// metric.navigationType navigate | reload | back-forward-cache …// entry.element 最大那块 DOM跨域图可能是 null// entry.url 图的地址如果是图// entry.size 面积// entry.id 元素自己的 id 属性可能没有})onINP((metric){constentrymetric.entries.find((e)e.durationmetric.value)||metric.entries[0]// metric.name INP// metric.value 毫秒最差或接近最差那次交互// metric.rating// entry.name click | keydown | pointerdown …// entry.duration 这次从输入到下一帧的延迟// entry.target 点到的节点可能拿不到})onCLS((metric){constentrymetric.entries[metric.entries.length-1]// metric.name CLS// metric.value 分数最大那一段窗口的累加// metric.rating// entry.value 这一跳自己的分数// entry.hadRecentInput true 表示刚输入完库计算时通常不计// entry.sources 哪些节点在动source.node 是 DOM})库做两件事用带buffered的PerformanceObserver收原料晚加载也能拿到已经发生的条目按 Chrome 同一套规则收成一个Metric。回调里已经是数不是要你自己从 entry 列表加总。delta是相对上次回调涨了多少上报终值看value。一句话web-vitals 负责把时间线收成三个数回调到了还不等于该发 HTTP。没有PerformanceObserver就静静返回不要补Date.now()假数。Chrome 的「好」大概是 LCP ≤ 2.5s、INP ≤ 200ms、CLS ≤ 0.1库会带上rating。1. 包装 API 听不到「画完了」第 2 篇能听到请求结束、脚本报错。首屏最大那张图何时画完不在XMLHttpRequest上。浏览器提供PerformanceObserver可以订阅largest-contentful-paint、layout-shift、交互用的event。web-vitals在这些条目上算出三个数。第 7 篇里的 SDK订的也是这三个函数。无埋点 XHRweb-vitals听谁被包装的open/sendPerformanceObserver、onLCP这类回调何时有数一次请求结束就定了页还开着数还能变你拿到的一次请求的字段已经收好的Metric2. 概念词典词是什么解决什么web-vitalsGoogle 的小库对外主要是onLCP/onINP/onCLS自己拼观察者时容易和 Chrome 的算法对不齐Metric回调里那份对象name、value、rating、id、entries、navigationType不用自己对阈值也不用自己翻是哪条 entryrating对照库内门槛good/needs-improvement/poorLCP 2500/4000INP 200/500CLS 0.1/0.25reportAllChanges值一变就回调默认关默认只在终值附近喊你一次buffered观察者能拿到订阅之前已经发生的条目脚本晚加载也不丢首屏 LCPINP 替代了旧的 FID第一次点击延迟。现在看整页交互里偏差的那一次不是第一次。新代码不要再订onFID。3. 怎么接到页面上npm 安装web-vitals和普通依赖一样import。不必抢在首屏脚本之前buffered能补到订阅前的条目。官方也建议把它放在业务代码之后。回调参数就是Metric。三个指标顶层字段相同字段含义nameLCP/INP/CLSvalue当前值。LCP、INP 是毫秒CLS 是分数rating对照上面的门槛id这一页这次指标的实例。从后退缓存恢复后会换新的entries算出这个值时用到的性能条目navigationType这次浏览怎么来的。解冻时是back-forward-cachedelta相对上次回调涨了多少。上报终值看valueentries里才是浏览器的原始条目三个指标不一样最后一条或对上value的那条上取什么用来干什么LCPelement、url、size知道该压哪张图路径用第 3 篇的htmlTreeAsString(entry.element)INPname事件类型、duration、target知道是点击还是按键、点了谁CLSsources[].node、hadRecentInput知道谁在跳LCP 的entries一般就一条就是当前最大的那块。INP 可能有多条用duration metric.value对上最差那次。跨域图、节点已经卸掉时element/target可能是null只剩url或什么都没有。埋点先留下这些就够name、value、rating、路由 name、DOM 路径。navigationType back-forward-cache的那次下一篇会丢掉。attribution 构建会多带摊平后的元素、交互信息大约多 1.5K。要查根因再换web-vitals/attribution。4. LCP最大块画完的时刻onLCP观察largest-contentful-paint。每来一条更大的内容就用它的startTime更新metric.value。页如果是预渲染的会减去激活时刻避免把用户没看见的那段算进去。页在用户看见之前就已经 hidden后台打开这条绘制不记。实现上是entry.startTime和firstHiddenTime比。LCP 不是一直听到关页。用户第一次真正的点击、按键或者页变成 hidden库会停观察、再回调一次终值。滚动也会让浏览器停发 LCP但程序触发的滚动不可靠库不拿滚动当停表信号。默认不设reportAllChanges时你往往只在停表附近收到一次。标题 800ms、大图 2.4s 那些中间值要自己开reportAllChanges: true才能在回调里看见。门槛写在库里[2500, 4000]。5. INP整页交互里偏差的那一次onINP观察 Event Timing 的event条目。浏览器不支持interactionId就直接返回没有数。一次交互的延迟是输入到下一帧绘制。库把各次交互收起来更新metric.value。交互很少时这个值就是最慢的那次交互很多时会取一个接近最差的代表值避免单次极端拖死整页。这和「支付按钮点击量」仍不是一回事。观察默认带durationThreshold: 40短于约 40ms 的交互不进这条观察。太短的第一次输入会再听first-input兜底以免整页零交互可报。页变成 hidden 时库把观察者里还没吐完的条目收完再强制回调一次。所以 INP 的终值通常也是离开或切走时才到你手上。门槛[200, 500]。6. CLS一段时间内位移的累加onCLS观察layout-shift。不是页面生命周期里所有位移加总而是按会话窗口收中间空档够长就另开一段value取最大的那段。刚输入完的位移hadRecentInput不计点开折叠面板不该算抖动。库会先等到 FCP 有了再报 CLS和 Chrome 自己的统计对齐。页 hidden 时同样收完剩余条目再回调一次。门槛[0.1, 0.25]。FCP 是第一次画出任意内容LCP 才是主内容。7. 回调到了还不等于该 POST三个函数的默认策略都偏向页还开着就继续改内部的Metric进后台或 LCP 停表时再喊你。开了reportAllChanges: true中间每一次变大都会进回调。不要在onLCP里直接track(EVENT)。同一页会留下一串越来越大的值服务端再聚合也分不清这一页到底有多慢。做法和第 1 篇的DURATION一样先放内存离开时发一次。这一段的完整口径在第 7 篇。bfcache 解冻时库会再给一套新id、navigationType back-forward-cache的数依据是pageshow的event.persisted。那套数往往偏快不要和第一次打开混在一起。第 7 篇写 SDK 为什么丢掉。v6 还在实验软导航SPA 里换页再测一套 LCP。本篇按硬加载讲。软导航是文档没换、路由换了和整页重载不是一回事。和前作怎么接篇补哪一段2无埋点包装fetch听不到「画完了」3行为栈entries里的节点仍用 DOM 路径1管线终值将走DURATION不是EVENT下一篇何时发信封、切标签不是后退缓存你可以从这里带走什么装web-vitals订onLCP/onINP/onCLS。脚本不必抢首屏观察者开了buffered。回调里是已经算好的Metric。看value和rating。LCP 最大元素在metric.entries里没有metric.element。LCP 听到更大的块就更新第一次点击或 hidden 时停INP 看交互延迟hidden 时出终值CLS 取最大的一段位移窗口。reportAllChanges只加密回调。何时track下一篇。没有 Performance API 就不要发假数。pay_duration用performance.measure不要冒充 LCP。仓库与相关文档GitHubweb-vitalssrc/onLCP.ts、src/onINP.ts、src/onCLS.ts本文按 v5.1.0指标定义web.dev/articles/vitals相关前作5框架钩子 · 4上报欢迎 Star、Issue 和 PR。本文是「前端数据埋点」第 6 篇web-vitals 如何把时间线收成三个数。不展开何时发信封、attribution 构建和软导航。
返回列表