Skip to content

需要采集什么指标?

  • RUM (Real User Monitoring) 指标,包括 FP, FCP, FMP, FID, MPFID,TTI。
  • Navigation Timing各阶段指标,包括 DNS, TCP, DOM 解析等阶段的指标。
  • 页面性能监控
  • 异常报错监控
  • 用户行为监控
  • 网络请求监控

阿里arms统计指标

image.pngimage.png

字节APMPlus指标

image.pngimage.pngimage.png

腾讯Aegis

image.png

RUM指标

以用户为中心的指标,根据google的web-vitals,其中有三个核心指标。

  • Largest Contentful Paint (LCP) - 最大内容绘制
  • First Input Delay (FID) 被INP取代 - 首次输入延迟
  • Cumulative Layout Shift (CLS)。- 累计布局偏移

INFO

web-vitals : LCP、CLS、INP / FCP、TTFB、TBT

RUM : 真实用户性能指标image.png

新版的核心指标是下面三个了, INP -> 交互绘制延迟

image.png

LCP-最大内容绘制

LCP(Largest Contentful Paint),即最大内容绘制时间测量加载性能

是 Core Web Vitals 度量标准,用于度量视口中最大的内容元素何时可见。它可以用来确定页面的主要内容何时在屏幕上完成渲染。 为了提供良好的用户体验,LCP 分数最好保证在 2.5 秒以内 image.png

  • 我们可以手写获取
typescript
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('LCP candidate:', entry.startTime, entry);
  }
}).observe({type: 'largest-contentful-paint', buffered: true});
typescript
import {onLCP} from 'web-vitals';

// Measure and log LCP as soon as it's available.
onLCP(console.log);

应用性能前端监控,字节跳动这些年经验都在这了 - 掘金timestamp-diagram.svg

FID-首次输入延迟(弃)

FID (First Input Delay )首次输入延迟,也叫首次交互时间,是度量用户第一次与页面交互的延迟时间,测量可交互性

FID 衡量的是从用户第一次与页面交互(例如,当他们点击链接,点击按钮,或使用自定义的 JavaScript 驱动的控件)到浏览器实际能够开始响应该交互的时间,为了提供良好的用户体验,站点应该努力使 FID 保持在 100 毫秒以内


注意:First Input Delay (FID) 不再是 Core Web Vitals 指标,已被Interaction to Next Paint (INP) 指标取代。因此,对 FID 的支持将于 2024 年 9 月 9 日结束。

typescript
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    const delay = entry.processingStart - entry.startTime;
    console.log('FID candidate:', delay, entry);
  }
}).observe({type: 'first-input', buffered: true});


// 使用web-vitals库
import {onFID} from 'web-vitals';

// Measure and log FID as soon as it's available.
onFID(console.log);

MPFID-最大可能首次输入延迟

MPFID(Max Potential FID),即最大首次输入延迟,测量的是用户可能经历的最坏的首次输入延迟,是FCP之后的 longest task。

typescript
let maxBlockingTime = 0;

new PerformanceObserver((list) => {
  const entries = list.getEntries();
  for (const entry of entries) {
    const blockingTime = entry.duration - 50;
    maxBlockingTime = Math.max(maxBlockingTime, blockingTime);
  }
  console.log('MPFID:', maxBlockingTime);
}).observe({ type: 'longtask', buffered: true });

INP 交互绘制延迟

INP (Interaction to Next Paint),它用于衡量用户交互到下一个页面渲染之间的时间。测量交互体验

它衡量的是「用户交互(如点击或按键)后到下次在页面上看到视觉更新之间经过的时间」,INP通常衡量页面上最差的输入延迟,建议网页的 INP 不得超过 200 毫秒image.png

typescript
import { onINP } from 'web-vitals';

onINP((inp) => {
  console.log('INP duration:', inp.duration);
  console.log('INP event:', inp.name); //指示哪个事件导致了INP的发生
  console.log('INP 开始时间:', inp.startTime); 
});

INP与FID的区别

INP 是 First Input Delay (FID) 的继任指标。

  • 虽然两者都是响应能力指标,但 FID 仅测量了页面上首次互动的输入延迟。
  • INP 通过考虑所有页面互动(从输入延迟到运行事件处理程序所需的时间,再到浏览器绘制下一帧)来改进 FID。
  • https://web.dev/articles/inp?hl=zh-cn

CLS-累计布局偏移

CLS (Cumulative Layout Shift ) 即累计布局偏移,用于衡量视觉稳定性

CLS 是衡量页面的整个生命周期中,发生的每次布局变化中的最大幅度的布局变化得分的指标。为了提供良好的用户体验,站点应该努力使 CLS 分数达到 0.1 或更低。 image.png

typescript
import {onCLS} from 'web-vitals';

// Measure and log CLS in all situations
// where it needs to be reported.
onCLS(console.log);
typescript
let clsValue = 0;
let clsEntries = [];
let sessionValue = 0;
let sessionEntries = [];
new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
        // Only count layout shifts without recent user input.
        if (!entry.hadRecentInput) {
            const firstSessionEntry = sessionEntries[0];
            const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
        // If the entry occurred less than 1 second after the previous entry and      
        // less than 5 seconds after the first entry in the session, include the      
        // entry in the current session. Otherwise, start a new session.  
         if (sessionValue
         && entry.startTime - lastSessionEntry.startTime < 1000
         && entry.startTime - firstSessionEntry.startTime < 5000) {
             sessionValue += entry.value;
             sessionEntries.push(entry);
          } else {
              sessionValue = entry.value;
              sessionEntries = [entry];
           }
           // If the current session value is larger than the current CLS value
           // update CLS and the entries contributing to it.
           if (sessionValue > clsValue) {
               clsValue = sessionValue;
               clsEntries = sessionEntries;
               // Log the updated value (and its entries) to the console. 
               console.log('CLS:', clsValue, clsEntries)
           }
       }
}}).observe({type: 'layout-shift', buffered: true});

FCP-首次内容绘制

FCP(First Contentful Paint)首次内容绘制 ,用于衡量感知的加载速度

它衡量了网页加载中用户可以看到屏幕上任何内容的第一个时间点,即浏览器渲染第一段 DOM 内容所用的时间 FCP 衡量的是从用户首次导航到相应网页到该网页的任何部分呈现在屏幕上所用的时间。

  • 网页上的文本、图片、非白色 <canvas> 元素及 SVG 都被视为 DOM 内容;
  • iframe 中的任何内容均_不会_包含在其中。

image.png

typescript
const entryHandler = (list) => {        
    for (const entry of list.getEntries()) {
        if (entry.name === 'first-contentful-paint') {
            observer.disconnect()
            console.log(entry)
        }
    }
}

const observer = new PerformanceObserver(entryHandler)
observer.observe({ type: 'paint', buffered: true })

TTFB-首字节时间

TTFB(Time To First Byte)加载第一个字节所需时间 (TTFB),即 首字节时间

正常来说,TTFB计算公式如下

typescript
// web-vitals定义
responseStart - startTime 
// 但是,这样子TTFB包含了TCP,DNS等各种时间,所以阿里arms中
// 将TTFB改为11
responseStart - requestStart 作为度量 请求响应耗时

且当前指标,一般放到请求性能模块,作为 **请求响应耗时。**参考阿里和字节,均是如此 image.png TTFB 是以下请求阶段的总和:

  • 重定向时间
  • Service Worker 启动时间(如果适用)
  • DNS 查找
  • 连接和 TLS 协商
  • 请求,直到响应的第一个字节到达

image.png

计算方式

typescript
new PerformanceObserver((entryList) => {
  const [pageNav] = entryList.getEntriesByType('navigation');

  console.log(`TTFB: ${pageNav.responseStart}`);
}).observe({
  type: 'navigation',
  buffered: true
});

web-vitals库

typescript
import {onTTFB} from 'web-vitals';

// Measure and log TTFB as soon as it's available.
onTTFB(console.log);

阿里arms - TTFB

image.png

web-vitals源码

typescript
export const onTTFB = (onReport: TTFBReportCallback, opts?: ReportOpts) => {
  // Set defaults
  opts = opts || {};

  let metric = initMetric('TTFB');
  let report = bindReporter(
    onReport,
    metric,
    TTFBThresholds,
    opts.reportAllChanges,
  );

  whenReady(() => {
    const navEntry = getNavigationEntry();

    if (navEntry) {
      const responseStart = navEntry.responseStart;

      if (isInvalidTimestamp(responseStart)) return;

      // The activationStart reference is used because TTFB should be
      // relative to page activation rather than navigation start if the
      // page was prerendered. But in cases where `activationStart` occurs
      // after the first byte is received, this time should be clamped at 0.
      // 核心在这里~~~!!!
      metric.value = Math.max(responseStart - getActivationStart(), 0);

      metric.entries = [navEntry];
      report(true);

      // Only report TTFB after bfcache restores if a `navigation` entry
      // was reported for the initial load.
      onBFCacheRestore(() => {
        metric = initMetric('TTFB', 0);
        report = bindReporter(
          onReport,
          metric,
          TTFBThresholds,
          opts!.reportAllChanges,
        );

        report(true);
      });
    }
  });
};
typescript
import {getNavigationEntry} from './getNavigationEntry.js';

export const getActivationStart = (): number => {
  const navEntry = getNavigationEntry();
  return (navEntry && navEntry.activationStart) || 0;
};

真实用户性能指标也就是上文有所提及的 RUM 以及平台自己扩展的一些额外的指标,包括以下指标:

  • 首次绘制时间(FP) :即 First Paint,为首次渲染的时间点。
  • 首次内容绘制时间(FCP) :即 First Contentful Paint,为首次有内容渲染的时间点。
  • 首次有效绘制时间(FMP) :用户启动页面加载与页面呈现首屏之间的时间。
  • 首次交互时间(FID) :即 First Input Delay,记录页面加载阶段,用户首次交互操作的延时时间。FID 指标影响用户对页面交互性和响应性的第一印象。
  • 交互中最大延时(MPFID) :页面加载阶段,用户交互操作可能遇到的最大延时时间。
  • 完全可交互时间(TTI):即 Time to interactive,记录从页面加载开始,到页面处于完全可交互状态所花费的时间。

TBT-总阻塞时间

Total Blocking Time (TBT) ,总阻塞时间 (TBT) 指标衡量在首次内容绘制 (FCP) 之后,主线程处于阻塞足够长的时间以防止输入响应所用的总时长。


TBT 衡量的是网页被禁止响应用户输入(例如鼠标点击、屏幕点按或键盘按下操作)的总时长。总和的计算方法是:将 First Contentful PaintTime to Interactive 之间所有长任务的_阻塞部分_相加。任何执行时间超过 50 毫秒的任务都是耗时较长的任务。50 毫秒之后的时间属于阻塞部分。例如,如果 Lighthouse 检测到时长为 70 毫秒的任务,则阻塞部分将为 20 毫秒。


注意:MPFID => Max Potential First Input Delay 已在 Lighthouse 6.0 中废弃。今后,不妨考虑在实验中使用 Total Blocking Time,并在字段中使用 First Input Delay 和 Interaction to Next Paint。

TTI-首次(完全)可交互时间

Time to Interactive (TTI)

TTI 指标用于衡量从网页开始加载到其主要子资源加载完成所用的时间,并且能够快速可靠地响应用户输入。

FP-白屏时间

image.png

typescript
window.performance.getEntriesByType('paint')
// or
window.performance.getEntriesByName('first-paint')
window.performance.getEntriesByName('first-contentful-paint')
typescript
const observer = new PerformanceObserver(function(list) {
  const perfEntries = list.getEntries();
  for (const perfEntry of perfEntries) {
      // Process entries
      // report back for analytics and monitoring
      // ...
  }
});

// register observer for paint timing notifications
// 早期entryTypes,现在推荐使用type字段+buffered了
observer.observe({entryTypes: ["paint"]});

DANGER

  1. entryTypes可以指定多种性能条目类型,但不能捕获调用前的事件;
  2. type只能指定单一一种性能条目类型,但可以通过buffered选项捕获调用前的事件。
typescript
// FP
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntriesByName('first-paint')) {
    console.log('fp', entry);
  }
}).observe({ type: 'paint', buffered: true });

// FCP 首次内容绘制
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntriesByName('first-contentful-paint')) {
    console.log('fcp', entry);
  }
}).observe({ type: 'paint', buffered: true });

FMP首次有效绘制(弃)

First Meaningful Paint (FMP) 首次有效绘制,首屏

DANGER

在实践中,FMP 对网页加载的细微差异过于敏感,从而导致结果不一致(双模)。 此外,该指标的定义取决于特定于浏览器的实现详情,这意味着该指标无法标准化,无法在所有网络浏览器中实现。今后,不妨考虑改用 Largest Contentful Paint。

Navigation Timing指标

image.png

typescript
// 首次渲染时间(白屏) => 参考arms
FP:responseEnd - fetchStart,
// TTI 首次可交互时间
TTI:domInteractive - fetchStart,
// 首包时间?arms里的,不要这个
// FirstByte:responseStart - domainLookupStart,

// 关键时间段
Redirect: redirectEnd - redirectStart,
Cache:domainLookupStart - fetchStart,
DNS: domainLookupEnd - domainLookupStart,
TCP: connectEnd - connectStart,
SSL: secureConnectionStart ? connectEnd - secureConnectionStart : 0,

// SSL链接建立好后,从客户端发送到服务端首次响应的耗时。
Request: responseStart - requestStart,
// 内容传输耗时,从服务端首次响应到数据完全响应完的耗时。
Response: responseEnd - responseStart, // 看arms解释-Trans
// HTML加载完成时间,DOM Ready时间
DomReady:domContentLoadedEventEnd - fetchStart,
// DomParse是DOM解析的第一个阶段,主要是构建DOM树
DomParse: domInteractive - responseEnd, // DOM解析
// Processing包含的范围更广,除了DOM解析,还包括资源的处理和事件的执行
// 下载解析CSS,JS,图片等
Processing:domComplete - domLoading,
// DOM解析完成后资源加载耗时 - arms => Res
ResourceLoad:loadEventStart-domContentLoadedEventEnd
// 页面及资源完全加载时间
Load:loadEventStart - fetchStart,

TTFB: responseStart - navigationStart, // 保持与web-vitals一致,