Skip to content

性能检测分析评估 ​

性能瓶颈

Web 性能是客观的衡量标准,是用户对加载时间和运行时响应速率的直观体验,既包括客观的度量如加载时间,每秒帧数和到页面可交互的时间;也包括用户的对页面内容加载时间的主观感觉。

基本概况 ​

性能检测与监控方案 ​

性能检测工具 ​

Waterfall(瀑布流)展示浏览器所做的不同工作,如运算布局,JavaScript,重绘,和垃圾回收 Call Tree(调用树)展示那些最耗时间的JavaScript函数 Flame Chart(火焰图)展示记录过程中的JavaScript调用堆栈

衡量网站响应能力的指标 ​

帧率:https://developer.mozilla.org/zh-CN/docs/Tools/Performance/Frame_rate 瀑布:https://developer.mozilla.org/zh-CN/docs/Tools/Performance/Waterfall

如何十分钟实现一个简单的前端性能、fetch请求实时监控? ​

https://segmentfault.com/a/1190000021655532

性能分析 ​

Core Web Vitals ​

Core Web Vitals 是由 Chrome 团队创建的一类特定指标。它们目前仅限于 3 个专注于用户体验的指标,即最大内容绘制(LCP)、累积布局偏移(CLS)、下一个绘制交互(INP)。这三个指标共同关注用户体验的加载、交互性和视觉稳定性。

  • Largest Contentful Paint (LCP):衡量加载性能。为了提供良好的用户体验,LCP 应在网页首次开始加载后的 2.5 秒内发生。
  • Interaction to Next Paint (INP):衡量互动。为了提供良好的用户体验,网页的 INP 应不超过 200 毫秒。
  • Cumulative Layout Shift (CLS):衡量视觉稳定性。为了提供良好的用户体验,网页应将 CLS 保持在 0.1 或更低。

最大内容绘制(LCP) ​

Largest Contentful Paint(LCP),即最大内容绘制,绘制出最大文本或图片的时间。 LCP指标用于记录视窗内最大的元素绘制的时间,该时间会随着页面渲染变化而变化,因为页面中的最大元素在渲染过程中可能会发生改变,另外该指标会在用户第一次交互后停止记录。

评分标准 ​

衡量加载性能。

  • 良好(≤ 2.5秒)
  • 需要改进(2.5秒 - 4秒)
  • 欠佳(> 4秒)

影响因素 ​

----------------------------------------------------------------------------------
  LCP 阶段	         说明
----------------------------------------------------------------------------------
  首字节时间 (TTFB)	  从用户开始加载网页到浏览器收到 HTML 文档响应的第一个字节的时间。
  加载延迟	          TTFB 与浏览器开始加载 LCP 资源之间的增量。
  加载时间	          加载 LCP 资源本身所需的时间。
  渲染延迟	          LCP 资源完成加载到 LCP 元素完全呈现之间的增量。
----------------------------------------------------------------------------------

与下一个绘制交互(INP) ​

Interaction to Next Paint(INP),即与下一个绘制交互,旨在通过选择用户访问页面时发生的最长交互之一来表示页面的整体交互延迟。

评分标准 ​

衡量互动。

  • 良好(≤ 200毫秒)
  • 需要改进(200毫秒 - 500毫秒)
  • 欠佳(> 500毫秒)

影响因素 ​

累积布局位移(CLS) ​

Cumulative Layout Shift(CLS),即累积布局位移,用于衡量视觉稳定性,CLS 测量整个页面生命周期内发生的所有意外布局偏移中最大一连串的布局偏移分数。
为了提供良好的用户体验,页面应保持0.1 的 CLS。或更少。

评分标准 ​

衡量视觉稳定性。

  • 良好(≤ 0.1)
  • 需要改进(0.1 - 0.25)
  • 欠佳(> 0.25)

影响因素 ​

  • 无尺寸的图像
  • 无尺寸的广告、嵌入和 iframe
  • 动态注入的内容
  • 导致不可见文本闪烁 (FOIT)/无样式文本闪烁 (FOUT) 的网络字体
  • 在更新 DOM 之前等待网络响应的操作

网页性能评估具体指标 ​

首次绘制(FP) ​

First Paint(FP),即首次绘制,页面第一次绘制像素的时间,实际也是白屏时间。

评分标准 ​

  • 良好(≤ 秒)
  • 需要改进(毫秒 - 毫秒)
  • 欠佳(> )

影响因素 ​

首字节时间(TTFB) ​

Time to First Byte (TTFB),即首字节时间,是从用户首次请求页面(单击链接或键入 URL)到第一个字节的数据从服务器返回到浏览器的时间。TTFB是衡量资源请求与响应的第一个字节开始到达之间的时间的度量。

评分标准 ​

  • 良好(≤ 0.8秒)
  • 需要改进(0.8秒 - 1.8秒)
  • 欠佳(> 1.8秒)

网络请求阶段及其相关时序图。startTimeTTFB 测量和之间经过的时间responseStart。

TTFB是以下请求阶段的总和 ​

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

影响因素 ​

  • 基础设施不足以处理高流量负载的托管服务
  • 内存不足可能导致抖动的 Web 服务器
  • 未优化的数据库表
  • 次优的数据库服务器配置
  • 缓慢的服务器响应速度
  • JavaScript 和 CSS 渲染阻塞
  • 资源加载时间
  • 客户端渲染

首次内容绘制(FCP) ​

First Contentful Paint (FCP) ,即首次内容绘制时间,指测量从页面开始加载到页面内容的任何部分呈现在屏幕上的时间。“内容”是指文本、图像(包括背景图像)、<svg>元素或非白色<canvas>元素,其中并未包含 iframe 中的任何内容。这个就是实际有意义的首屏时间,也就是我们通常要优化的首屏时间。

评分标准 ​

  • 良好(≤ 1.8秒)
  • 需要改进(1.8秒 - 3秒)
  • 欠佳(> 3秒)

在上方的加载时间轴中,FCP 发生在第二帧,因为那是首批文本和图像元素在屏幕上完成渲染的时间点。

影响因素 ​

  • 字体加载时间

总阻塞时间(TBT) ​

Total Blocking Time(TBT),即页面阻塞总时长,总阻塞时间 (TBT) 指标测量First Contentful Paint 首次内容绘制 (FCP)与Time to Interactive 可交互时间 (TTI)之间的总时间,这期间,主线程被阻塞的时间过长,无法作出输入响应。

  • 当任务用时超过 50 毫秒时计算 FCP 和 Time to Interactive 之间的所有时间段的总和,以毫秒表示。任何执行时间超过 50 毫秒的任务都是耗时较长的任务。50 毫秒后的时间是阻塞部分。

评分标准 ​

  • 良好(≤ 0.2秒)
  • 需要改进(0.2秒 - 0.6秒)
  • 欠佳(> 0.6秒)

影响因素 ​

  • 不必要的 JavaScript 加载、解析或执行。
  • 低效的 JavaScript 语句。例如,分析“性能”面板中的代码后,假设您看到对 document.querySelectorAll('a') 的调用会返回 2000 个节点。

耗时较长任务分析 ​

可交互时间(TTI) ​

Time to Interactive(TTI),即可交互时间,TTI 指标测量页面从开始加载到主要子资源完成渲染,并能够快速、可靠地响应用户输入所需的时间。

评分标准 ​

  • 良好(≤ 秒)
  • 需要改进(毫秒 - 毫秒)
  • 欠佳(> )

影响因素 ​

首次输入延迟 (FID) ​

First Input Delay(FID),即首次输入延迟,用于衡量页面交互性,FID 测量从用户第一次与页面交互(例如当他们单击链接、点按按钮或使用由 JavaScript 驱动的自定义控件)直到浏览器对交互作出响应,并实际能够开始处理事件处理程序所经过的时间。

评分标准 ​

  • 良好(≤ 100毫秒)
  • 需要改进(100毫秒 - 300毫秒)
  • 欠佳(> 300毫秒)

影响因素 ​

首次有效渲染时间 (FMP) ​

速度指数(SI) ​

Speed Index(SI),即速度指数,表明了网页内容的可见填充速度,衡量页面加载期间内容在视觉上显示的速度。

评分标准 ​

  • 良好(≤ 3.4秒)
  • 需要改进(3.4秒 - 5.8秒)
  • 欠佳(> 5.8毫秒)

影响因素 ​

  • 尽量减少主线程工作
  • 缩短 JavaScript 执行时间
  • 确保文本在网页字体加载期间保持可见状态

三个传统性能指标 ​

文档加载时间(DLT) ​

Document Load Time(DLT),即文档加载时间,DOMContentLoaded事件。该指标与DOM内容加载时间(DCL)相同,在此不做详述。

DOM内容加载时间(DCL) ​

DOM Content Loaded(DCL),即DOM内容加载时间,当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。

document.addEventListener("DOMContentLoaded", function(event) {
  console.log("DOM fully loaded and parsed");
});

document.addEventListener('readystatechange', (event) => {
  log.textContent = log.textContent + `readystate: ${document.readyState}\n`;
});

for(var i=0; i<1000000000; i++){
  // 这个同步脚本将延迟 DOM 的解析。
  // 所以 DOMContentLoaded 事件稍后将启动。
}

页面加载时间(PLT) ​

Page Load Time(PLT),即页面加载时间,浏览器中加载旋转图标停止消失的时间,即onload事件。当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发load事件。

window.addEventListener('load', (event) => {
  console.log('page is fully loaded');
});

网页性能检测分析工具 ​

WebPageTest网页性能测试工具 ​

WebPageTest网页性能测试工具,是一种可提供有关页面在各种条件下的性能的深入诊断信息,同时每个测试都可以在世界各地的不同位置、在真实浏览器上、在任意数量的可定制网络条件下运行的的 Web 性能工具,能够为用户构建高质量的网络体验。

观察相关指标 ​


Connection ​

Waterfall ​

网页性能测试结果 ​

YSlow辅助插件 ​

Yslow的网页分析机制是基于Yahoo高性能网站34条规则中可测试规则中的23条对网页进行评分,并提供了改进页面性能的相关建议。

23条性能测试规则 ​

1.最小化 HTTP 请求2.使用内容交付网络3.避免空 src 或 href
4.添加过期或缓存控制标头5.Gzip 组件6.将样式表放在顶部
7.将脚本放在底部8.避免使用 CSS 表达式9.使 JavaScript 和 CSS 外部化
10.减少 DNS 查找11.缩小 JavaScript 和 CSS12.避免重定向
13.删除重复的脚本14.配置 ETag15.使 AJAX 可缓存
16.将 GET 用于 AJAX 请求17.减少 DOM 元素的数量18.没有 404
19.减小 Cookie 大小20.为组件使用无 Cookie 域21.避免过滤器
22.不要在 HTML 中缩放图像23.使 favicon.ico 小且可缓存

Chrome Devtools ​

通过“渲染”选项卡上的性能相关选项参考发现渲染性能问题 ​

浏览器右上角扩展图标,"更多工具" -> "开发者工具", 打开开发工具。 按Command+ Shift+ P(Mac) 或Control+ Shift+ P(Windows、Linux、ChromeOS) 打开命令菜单。 开始输入rendering,选择Show Rendering,然后按Enter。DevTools在您的 DevTools 窗口底部显示“渲染”选项卡。

  • 查看正在重新绘制的区域
  • 查看页面上布局变化的位置和时间
  • 查看图层边框和图块的叠加
  • 实时查看每秒帧数
  • 识别具有与滚动相关的事件侦听器的页面元素

performance-insights性能洞察 ​

性能洞察面板支持记录一般和用例驱动(例如页面加载)的性能。

注意事项

节流与您的计算机功能有关。例如,4x 减速选项使您的 CPU 运行速度比其通常的能力慢 4 倍。DevTools 无法真正模拟移动设备的 CPU,因为移动设备的架构与台式机和笔记本电脑的架构有很大不同。

使用Layout Shifts轨道来识别潜在的布局变化问题和原因。

运行时性能 ​

CPU使用情况:
JS堆大小:
DOM节点:
JS事件监听器:
文档:
文档框架:
布局个数/秒:
样式重新计算次数/秒:

Lighthouse ​

运行 Lighthouse 的方式有两种:作为 Chrome 扩展程序运行,或作为命令行工具运行。 Chrome 扩展程序提供了一个对用户更友好的界面,方便读取报告。 命令行工具允许您将 Lighthouse 集成到持续集成系统。

Lighthouse分值计算器 ​

基于FCP(First Contentful Paint)、SI(Speed Index)、LCP(Largest Contentful Paint)、TBT (Total Blocking Time)、CLS (Cumulative Layout Shift)五大核心指标计算Lighthouse的统计分值。

  • 0 to 49 (red): Poor
  • 50 to 89 (orange): Needs Improvement
  • 90 to 100 (green): Good

Lighthouse Treemap ​

Lighthouse Treemap,即树状图,展示了javascript资源的加载与使用分布图示效果。

PageSpeed tools ​

CrUX Vis ​

可视化真实世界网络性能数据的时间序列

性能测量相关信息 ​

除了 Navigation Timing,W3C Performance Group 还标准化了另外两个 API:User Timing 和 Resource Timing

Performance 接口 ​

已弃用,Performance 接口可以获取到当前页面中与性能相关的信息。它是 High Resolution Time API 的一部分,同时也融合了 Performance Timeline API、Navigation Timing API、 User Timing API 和 Resource Timing API。

console.info(window.performance);



Performance.clearMarks():从浏览器的性能入口缓冲区中删除给定的标记。
Performance.clearMeasures():从浏览器的性能条目缓冲区中删除给定的度量。
Performance.clearResourceTimings():从浏览器的性能数据缓冲区中删除所有performance entries带有entryType" resource" 的内容。
Performance.getEntries():返回PerformanceEntry基于给定filter的对象列表。
Performance.getEntriesByName():PerformanceEntry根据给定的名称和条目类型返回对象列表。
Performance.getEntriesByType():返回PerformanceEntry给定条目类型的对象列表。
Performance.mark():使用给定名称timestamp在浏览器的性能条目缓冲区中创建一个。
Performance.measure():timestamp在浏览器的性能入口缓冲区中的两个指定标记(分别称为开始标记和结束标记)之间创建一个命名。
Performance.now():返回DOMHighResTimeStamp表示自参考时刻起经过的毫秒数。
Performance.setResourceTimingBufferSize():将浏览器的资源计时缓冲区大小设置为指定数量的“ resource”对象。
Performance.toJSON():是一个 jsonizer 返回一个表示Performance对象的 json对象。
let time = performance.timing;

let loadTime = time.loadEventEnd - time.loadEventStart;

timingObj['重定向时间'] = (time.redirectEnd - time.redirectStart) / 1000;
timingObj['DNS解析时间'] = (time.domainLookupEnd - time.domainLookupStart) / 1000;
timingObj['TCP完成握手时间'] = (time.connectEnd - time.connectStart) / 1000;
timingObj['HTTP请求响应完成时间'] = (time.responseEnd - time.requestStart) / 1000;
timingObj['DOM开始加载前所花费时间'] = (time.responseEnd - time.navigationStart) / 1000;
timingObj['DOM加载完成时间'] = (time.domComplete - time.domLoading) / 1000;
timingObj['DOM结构解析完成时间'] = (time.domInteractive - time.domLoading) / 1000;
timingObj['脚本加载时间'] = (time.domContentLoadedEventEnd - time.domContentLoadedEventStart) / 1000;
timingObj['onload事件时间'] = (time.loadEventEnd - time.loadEventStart) / 1000;


const Perf = window.performance = {
  eventCounts: {
    size: 36
  },
  memory: {   // 提供了一个具有基本内存使用信息的对象。
    jsHeapSizeLimit: 4294705152,
    totalJSHeapSize: 3242548,
    usedJSHeapSize: 1217256
  },
  navigation: {  // 提供有关 中列出的时间中包含的操作的有用上下文timing,包括页面是加载还是刷新,发生了多少重定向,等等。
    redirectCount: 0,
    type: 0
  },
  onresourcetimingbufferfull: null
  timeOrigin: null,    // 返回性能测量开始时间的高分辨率时间戳。s
  timing: {
    connectEnd: 1636424463192                    // 返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。
    connectStart: 1636424463192                  // 返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。
    domComplete: 1636424465714                   // 返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange (en-US) 被触发时的Unix毫秒时间戳。
    domContentLoadedEventEnd: 1636424465712      // 返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。
    domContentLoadedEventStart: 1636424465711    // 返回当解析器发送DOMContentLoaded (en-US) 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。

    domInteractive: 1636424465711                // 返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange (en-US)事件触发时)的Unix毫秒时间戳。
    domLoading: 1636424465680                    // 返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange (en-US)事件触发时)的Unix毫秒时间戳。
    domainLookupEnd: 1636424463192               // 表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。
    domainLookupStart: 1636424463192             // 表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。
    fetchStart: 1636424463192                    // 表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。

    loadEventEnd: 1636424465714                   // 返回当load (en-US)事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0.
    loadEventStart: 1636424465714                 // 返回该文档下,load (en-US)事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。
    navigationStart: 1636424463186                // 表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同。
    redirectEnd: 0                                // 表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.
    redirectStart: 0                              // 表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

    requestStart: 1636424463192                   // 返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。
    responseEnd: 1636424465686                    // 返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。
    responseStart: 1636424463192                  // 返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。
    secureConnectionStart: 0                      // 返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。
    unloadEventEnd: 0                             // 表征了unload (en-US)事件处理完成时的UNIX时间戳。
    unloadEventStart: 0                           // 表征了unload (en-US)事件抛出时的UNIX时间戳。
  }
}

PerformanceObserver ​

PerformanceObserver.supportedEntryTypes ​

在observer.observe中的 type 类型值

entryTypes: [
  "element",
  "event",
  "first-input",
  "largest-contentful-paint",
  "layout-shift",
  "long-animation-frame",
  "longtask",
  "mark",
  "measure",
  "navigation",
  "paint",
  "resource",
  "visibility-state",
]
function runAfterFirstPaint(callback) {
  const observer = new PerformanceObserver((list) => {
    const entries = list.getEntries();
    // 收集已经触发过的指标,防止重复回调
    const fired = new Set();

    for (const entry of list.getEntries()) {
      // 只监听 FP / FCP
      if (
        entry.name !== "first-paint" &&
        entry.name !== "first-contentful-paint"
      ) {
        continue;
      }

      if (fired.has(entry.name)) continue;

      fired.add(entry.name);

      // 把【指标名称 + 真实渲染时间】传给回调
      callback(
        entry.name,
        entry.startTime, // 核心:FP/FCP发生时的时间戳(ms)
      );

      // 两个指标都拿到后停止监听
      if (fired.size === 2) {
        observer.disconnect();
      }
    }
  });

  // buffered: true 可以捕获已经发生过的 paint 事件(页面加载完后再调用函数也能拿到数据)
  observer.observe({ type: "paint", buffered: true });
}

runAfterFirstPaint((name, startTime) => {
  console.log(`[前置]🎉 ${name}:`, startTime, formatDateTimeUtil());
});

PerformanceNavigationTiming ​

推荐,PerformanceNavigationTiming 提供了用于存储和检索有关浏览器文档事件的指标的方法和属性。例如,此接口可用于确定加载或卸载文档需要多少时间。

PerformanceResourceTiming ​

PerformanceResourceTiming 接口可以检索和分析有关加载应用程序资源的详细网络计时数据。应用程序可以使用 timing 指标来确定获取特定资源所需的时间长度,例如XMLHttpRequest,<SVG>,image 或 script。

PerformanceEntry ​

web-vitals JS库 ​

import { getTTFB, getCLS, getFID, getFCP, getLCP } from 'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify(metric);
  // Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
  (navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) ||
      fetch('/analytics', {body, method: 'POST', keepalive: true});
}

getTTFB(sendToAnalytics);
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);

addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    flushQueue();
  }
});

addEventListener('pagehide', flushQueue);

内存问题 ​

内存检查器 ​

Released under the MIT License.