性能优化 - 前端性能监控和性能指标计算方式

性能优化 - 前端性能监控和性能指标计算方式

  • 前言
  • 一. 性能指标介绍
    • 1.1 单一指标介绍
    • 1.2 指标计算
      • ① Redirect(重定向耗时)
      • ② AppCache(应用程序缓存的DNS解析)
      • ③ DNS(DNS解析耗时)
      • ④ TCP(TCP连接耗时)
      • ⑤ TTFB(请求响应耗时)
      • ⑥ Trans(内容传输耗时)
      • ⑦ DOM(DOM解析耗时)
    • 1.3 FP(first-paint) 和 FCP(first-contentful-paint)
    • 1.4 LCP(Largest Contentful Paint)
    • 1.5 LongTask长任务统计
  • 二. 性能指标计算测试
    • 2.1 衡量网络请求响应时间的指标
    • 2.2 衡量页面加载速度的指标
    • 2.3 TTI(Time to Interactive)衡量页面可交互性的指标
    • 2.4 TBT(Total Blocking Time)
    • 2.5 总结

前言

利用LightHouse进行合理的页面性能优化 这篇文章主要讲解了如何使用Lighthouse。 这里把相关图片再展示一下:
在这里插入图片描述
我们可以看到Lighthouse计算的时候,会根据这几个维度的指标来计算总分。那么本篇文章,就主要讲解下前端性能监控相关的重要指标含义和计算方式。

一. 性能指标介绍

在介绍指标之前,我们首先应当知道这些数据可以从哪里获取。JS里面,有一个 performance 对象,它是专门用来用于性能监控的对象,内置了一些前端需要的性能参数。

我们随便打开一个浏览器,在终端控制台输入以下内容:

performance.getEntriesByType('navigation')

如图:
在这里插入图片描述

1.1 单一指标介绍

  1. navigationStart:导航开始的时间,即浏览器开始获取页面的时间。
  2. redirectCount:重定向次数,表示在导航过程中发生的重定向次数。
  3. type:导航类型,可能的取值有:
    • navigate:常规导航,例如用户点击链接或输入URL进行的导航。
    • reload:页面重新加载。
    • back_forward:通过浏览器的前进或后退按钮导航。
  4. unloadEventStart:前一个页面的unload事件开始的时间。
  5. unloadEventEnd:前一个页面的unload事件结束的时间。
  6. redirectStart:重定向开始的时间。
  7. redirectEnd:重定向结束的时间。
  8. fetchStart:浏览器开始获取页面资源的时间。
  9. domainLookupStart:域名解析开始的时间。
  10. domainLookupEnd:域名解析结束的时间。
  11. connectStart:建立与服务器连接开始的时间。
  12. connectEnd:建立与服务器连接结束的时间。
  13. secureConnectionStart:安全连接开始的时间,如果不是安全连接,则该值为0。
  14. requestStart:向服务器发送请求的时间。
  15. responseStart:接收到服务器响应的时间。
  16. responseEnd:接收到服务器响应并且所有资源都已接收完成的时间。
  17. domLoading:开始解析文档的时间。
  18. domInteractive:文档解析完成并且所有子资源(例如图片、样式表等)也已加载完成的时间。
  19. domContentLoadedEventStartDOMContentLoaded事件开始的时间,表示HTML文档解析完成并且所有脚本文件已下载完成。
  20. domContentLoadedEventEndDOMContentLoaded事件结束的时间,表示所有脚本文件已执行完成。
  21. domComplete:文档和所有子资源(例如图片、样式表等)都已完成加载的时间。
  22. loadEventStartload事件开始的时间,表示所有资源(包括图片、样式表、脚本文件等)都已加载完成。
  23. loadEventEndload事件结束的时间,表示所有资源(包括图片、样式表、脚本文件等)都已执行完成。

1.2 指标计算

我们看下图
在这里插入图片描述

我们从上图出发,分别对各个阶段进行计算,我们说下几个比较重要的阶段,按照从左往右的顺序。

① Redirect(重定向耗时)

表示从重定向开始(redirectStart)到重定向结束的时间(redirectEnd)的时间间隔,它反映了浏览器在这段时间内完成了重定向的过程:

const redirectTime = redirectEnd - redirectStart

② AppCache(应用程序缓存的DNS解析)

这一部分也是在进行DNS解析,在使用AppCache(应用程序缓存)的情况下,浏览器会在加载页面时检查缓存中是否存在相应的资源,并根据需要更新缓存:

const appcacheTime = domainLookupStart - fetchStart

③ DNS(DNS解析耗时)

DNS解析耗时:在浏览器加载网页时,当需要与服务器建立连接时,浏览器会首先进行DNS解析,将域名转换为对应的IP地址。DNS解析的过程包括向DNS服务器发送查询请求、等待DNS服务器响应以及获取到IP地址。

dns = domainLookupEnd - domainLookupStart

④ TCP(TCP连接耗时)

TCP连接耗时:在浏览器加载网页时,当浏览器需要与服务器建立连接时,它会向服务器发送请求,并等待服务器响应。建立连接的过程包括TCP握手、SSL握手等。

tcp = connectEnd - connectStart

其中还有建立SSL连接的时间,包括在TCP耗时里面。

ssl = connectEnd - secureConnectionStart

⑤ TTFB(请求响应耗时)

请求耗时:从发送请求到接收到服务器响应的第一个字节所花费的时间。

ttfb = responseStart - requestStart

⑥ Trans(内容传输耗时)

当浏览器发送请求后,服务器会返回相应的响应,这个差值就是衡量浏览器接收服务器响应的耗时。

trans = responseEnd - responseStart

⑦ DOM(DOM解析耗时)

DOM这一块比较复杂,实际上还能分成3个小DOM阶段。

  1. 阶段一(注意,上图中并没有显式地展示出来):解析DOM阶段。
const dom1 = domInteractive - responseEnd
  1. 阶段二:文档解析完成,html、js解析完成,css、图片加载完成。即加载DOM阶段。
const dom2 = domComplete-domInteractive
  1. 阶段二当中还可以分出一小个阶段:代表从开始加载DOM内容到DOM内容加载完成的时间间隔。
const domLoaded = domContentLoadedEventEnd - domContentLoadedEventStart

1.3 FP(first-paint) 和 FCP(first-contentful-paint)

FP(first-paint)FCP(first-contentful-paint)

FP指的是浏览器首次将像素渲染到屏幕上的时间点,即页面开始渲染的时间点。通常情况下,FP是指浏览器首次绘制任何可见的内容,包括背景色、文字、图片等,但不包括用户界面的控件,比如滚动条、按钮等。

FCP指的是浏览器首次将页面的有意义的内容渲染到屏幕上的时间点,即页面开始呈现有意义的内容的时间点。有意义的内容可以是文本、图片、视频等,但不包括背景色、边框等无意义的内容。

一般情况下,两者基本上没有什么区别,来说下两者的获取方式:

const fp = performance.getEntriesByName('first-paint')[0].startTime
const fcp = performance.getEntriesByName('first-contentful-paint')[0].startTime

后面我们都只说FCP

1.4 LCP(Largest Contentful Paint)

LCP:Largest Contentful Paint它表示在页面加载过程中,最大的可见内容元素(例如图片、视频、文本块等)加载完成并呈现在屏幕上的时间点。,是测量加载速度感知的重要指标之一。

获取方式,我们主要通过Performance 来进行监听:

new PerformanceObserver((entryList) => {
    var maxSize = 0;
    var renderTime = 0;
    for (var entry of entryList.getEntries()) {
        // 渲染的内容看最大值
        if(entry.size > maxSize){
            maxSize = entry.size;
            renderTime = entry.startTime;
        }
    }
    console.log('LCP', renderTime)
}).observe({type: 'largest-contentful-paint', buffered: true});

1.5 LongTask长任务统计

LongTask(长任务)是指在JavaScript主线程上执行时间超过50毫秒的任务。这些任务可能是复杂的计算、大量数据处理、DOM操作或其他耗时的操作。

我们可以通过以下方式来获取:

new PerformanceObserver((entryList) => {
    var list = entryList.getEntries();
    var entry = list[list.length-1];
    if(entry){
        console.log('LongTask',entry.startTime)
    }
}).observe({type: 'longtask', buffered: true});

一般我们取最后一个就是长任务的总耗时。这个值越低,性能越高。

二. 性能指标计算测试

贴出案例代码:

function test() {
    const entry = performance.getEntriesByType('navigation')[0]
    const {
        domComplete, secureConnectionStart, domInteractive, domContentLoadedEventStart, domainLookupEnd,
        domainLookupStart, connectEnd, connectStart, responseStart, requestStart, responseEnd, loadEventStart, domContentLoadedEventEnd, fetchStart, redirectEnd, redirectStart
    } = entry
    // redirectTime
    const redirectTime = redirectEnd - redirectStart
    // appcacheTime
    const appcacheTime = domainLookupStart - fetchStart
    // DNS解析时间
    const dnsTime = domainLookupEnd - domainLookupStart
    // TCP建立时间
    const tcpTime = connectEnd - connectStart
    // ssl 时间
    const sslTime = connectEnd - secureConnectionStart
    // requestTime 读取页面第一个字节的时间(请求时间)
    const requestTime = responseStart - requestStart
    // 返回响应时间
    const responseTime = responseEnd - responseStart
    // domContentLoadedEventEnd - domContentLoadedEventStart
    const domLoaded = domContentLoadedEventEnd - domContentLoadedEventStart
    // loadEventEnd - loadEventStart
    const loadTime = loadEventStart - domContentLoadedEventEnd
    const dom1 = domInteractive - responseEnd
    // 解析dom树耗时
    const dom2 = domComplete - domInteractive
    console.log('********各个阶段的消耗耗时********')
    console.log('redirectTime', redirectTime)
    console.log('appcacheTime', appcacheTime)
    console.log('dnsTime', dnsTime)
    console.log('tcpTime', tcpTime, '其中包括ssl时间', sslTime)
    console.log('TTFB(请求响应耗时)', requestTime)
    console.log('Trans(内容传输耗时)', responseTime)
    console.log('解析`DOM`阶段', dom1)
    console.log('加载`DOM`阶段', dom2, '其中包括(domContentLoadedEventEnd - domContentLoadedEventStart)', domLoaded)
    console.log('load事件耗时', loadTime)


    console.log('********校验时间差********')
    console.log('***********校验 responseStart - fetchStart差值**************');
    console.log('responseStart - fetchStart', responseStart - fetchStart)
    console.log('appCache + dns+ tcp + requestTime 总和:', appcacheTime + dnsTime + tcpTime + requestTime)

    console.log('***********校验 domInteractive - fetchStart差值**************');
    const tmp = appcacheTime + dnsTime + tcpTime + requestTime + responseTime
    const diff = domInteractive - fetchStart
    console.log('domInteractive - fetchStart', diff)
    console.log( 'appCache + dns+ tcp + request + response, 总和:', tmp, ', 空白时间', diff - tmp)
    console.log('空白时间(就是文档解析和构建DOM树的过程),即DOM1(domInteractive - responseEnd)', dom1)

    // 算一下domCompelete - fetchStart
    console.log('domCompelete - fetchStart', domComplete - fetchStart)

    console.log('********FCP********')
    const fcp = performance.getEntriesByName('first-contentful-paint')[0].startTime
    console.log("LCP(通过performance.getEntriesByName('first-contentful-paint')计算出来的)", fcp)

    console.log('********LCP********')
    new PerformanceObserver((entryList) => {
        var maxSize = 0;
        var renderTime = 0;
        for (var entry of entryList.getEntries()) {
            // 渲染的内容看最大值
            if (entry.size > maxSize) {
                maxSize = entry.size;
                renderTime = entry.startTime;
            }
        }
        console.log('LCP', renderTime)
    }).observe({ type: 'largest-contentful-paint', buffered: true });
    console.log('********LongTask********')
    new PerformanceObserver((entryList) => {
        var list = entryList.getEntries();
        var entry = list[list.length - 1];
        if (entry) {
            console.log('LongTask', entry.startTime)
        }
    }).observe({ type: 'longtask', buffered: true });
}

复制这段代码到浏览器中,然后运行test()即可,结果如下:(FCP打印错了)
在这里插入图片描述

代码里主要打印了各个阶段的耗时时长。我们主要看下下半部分的校验部分。再把上面的图搬过来对照着看:
在这里插入图片描述

2.1 衡量网络请求响应时间的指标

从发起网络请求(fetchStart)到服务器开始响应(responseStart)的时间间隔。

  • responseStart - fetchStart的差值为257毫秒。
  • 而这个差值由:appCache + dns+ tcp + requestTime 4个阶段连接而成,4个阶段的时间总和为256.5毫秒,基本上接近。

2.2 衡量页面加载速度的指标

从发起网络请求(fetchStart)到DOM解析完成(domInteractive)的时间间隔

  • domInteractive - fetchStart的差值为1328毫秒。
  • 而这个差值由:appCache + dns+ tcp + request + response + 空白时间 部分组成(空白时间就是上图的红色框部分)。
  • 我们可以看到,前5个区域的时间总和大概是:480毫秒。空白时间则848毫秒。
  • 我们又计算了domInteractive - responseEnd的差值,实际上就是DOM1,也就是加载DOM的时间,时间差为848毫秒,相吻合。

综上所述:

  • 页面加载速度的指标可以由:DNS+TCP+Request+Response+DOM加载完毕耗时 的总和来决定。

另外我们还能看出来,LCP的计算可以几乎为:domInteractive - fetchStart, 当然你用PerformanceObserver进行监听也是可以的。

这里代表页面加载速度,此时DOM仅仅是解析完成,如果想看DOM也加载完成的耗时,看指标domComplete - fetchStart

2.3 TTI(Time to Interactive)衡量页面可交互性的指标

TTI:表示从页面开始加载到用户可以与页面进行交互的时间。它的计算方式如下:

  1. FCP 时间为起始时间
  2. 查找到指示有5s的静默窗口时间(没有长任务并且不超过两个正在执行的GET请求)。
  3. 向后搜索静默窗口前的最后一个长任务,如果没有找到长任务,则在FCP上停止。
  4. TTI 是在安静窗口之前最后一个长任务的结束时间(如果没有找到长任务,则与FCP相同)

在这里插入图片描述

建议大家使用谷歌官方提供的:tti-polyfill

import ttiPolyfill from './path/to/tti-polyfill.js';

ttiPolyfill.getFirstConsistentlyInteractive(opts).then((tti) => {
  // Use `tti` value in some way.
});

当然,也可以使用一种较为粗略的方式来计算:

  1. 首先我们理解一下TTI,是从页面开始加载到用户可以与页面进行交互的时间。
  2. 页面开始加载,我们是不是可以看做fetchStart的时间。
  3. 页面进行交互的时间,那这个时候dom肯定是加载完毕了。我们按照非常极限的思路去想,这个是不是可以看做dom加载完毕的时间点,即domComplete
  4. 那么TTI ≈ domComplete - fetchStart

例如我用Lighthouse计算出来的TTI
在这里插入图片描述
使用:domComplete - fetchStart 计算出来的值:

function getTTI(){
    const entry = performance.getEntriesByType('navigation')[0]
    const {
        domComplete,
        fetchStart
    } = entry

    console.log('TTI', domComplete - fetchStart)
}
getTTI()

结果如下:
在这里插入图片描述

2.4 TBT(Total Blocking Time)

TBT就是衡量从FCP时间点到TTI这个时间点的时间区间内,所有超过50毫秒的长任务的总耗时。(这个看下来难以通过编码的方式来实现计算,也无法预估)

2.5 总结

  1. 我们在为页面做性能监控的时候,LCPFCP是我们的几个重要关注对象。
  2. LCP可以通过PerformanceObserver进行检测。
  3. FCP可以通过performance.getEntriesByName('first-contentful-paint')[0].startTime获取。
  4. 页面性能的发部分数据都可以从performance.getEntriesByType('navigation')[0]这里面获取到。
  5. 如果你想衡量网络请求响应时间的指标:responseStart - fetchStart,代表从发起网络请求(fetchStart)到服务器开始响应(responseStart)的时间间隔。
  6. 如果你想衡量页面加载速度的指标:domInteractive - fetchStart,代表从发起网络请求(fetchStart)到DOM解析完成(domInteractive)的时间间隔。
  7. domComplete - fetchStart 这个差值基本上囊括了最核心的部分。包括了从开始获取页面资源到 DOM 解析完成的整个过程,其中包括了网络请求、资源加载、解析 HTML、构建 DOM 树等操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/53194.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安全测试国家标准解读——并发程序安全

本系列文章主要围绕《GB/T 38674—2020 信息安全技术 应用软件安全编程指南》进行讲解,该标准是2020年4月28日,由国家市场监督管理总局、国家标准化管理委员会发布,2020年11月01日开始实施。我们对该标准中一些常见的漏洞进行了梳理&#xff…

内核链表在用户程序中的移植和使用

基础知识 struct list_head {struct list_head *next, *prev; }; 初始化: #define LIST_HEAD_INIT(name) { (name)->next (name); (name)->prev (name);} 相比于下面这样初始化,前面初始化的好处是,处理链表的时候,不…

iTOP-RK3568开发板Docker 安装 Ubuntu 18.04

Docker 下载安装 Ubuntu18.04,输入以下命令: sudo apt update docker pull ubuntu:18.04 切换 Shell 到 Ubuntu 18.04,输入以下命令: docker container run -p 8000:3000 -it ubuntu:18.04 /bin/bash -p 参数:容器的…

初学HTML:采用CSS绘制一幅夏天的图

下面代码使用了HTML和CSS来绘制一幅炎炎夏日吃西瓜的画面。其中&#xff0c;使用了伪元素和阴影等技巧来实现部分效果。 <!DOCTYPE html> <html> <head><title>炎炎夏日吃西瓜</title><style>body {background-color: #add8e6; /* 背景颜…

[UE4][C++]调整分屏模式下(本地多玩家)视口的显示位置和区域

一、分屏模式设置 在UE4中&#xff0c;多个玩家共用一个显示器就可以启用分屏模式&#xff0c;按玩家人数&#xff08;最大四人&#xff09;将屏幕均匀分割&#xff0c;显示不同玩家的视角&#xff0c;开发者可以在编辑器里设置分割类型&#xff08;水平或者垂直&#xff09;&a…

Redis如何实现排行榜?

今天给大家简单聊聊 Redis Sorted Set 数据类型底层的实现原理和游戏排行榜实战。特别简单&#xff0c;一点也不深入&#xff0c;也就 7 张图&#xff0c;粉丝可放心食用&#xff0c;哈哈哈哈哈~~~~。 1. 是什么 Sorted Sets 与 Sets 类似&#xff0c;是一种集合类型&#xff…

分治法 Divide and Conquer

1.分治法 分治法&#xff08;Divide and Conquer&#xff09;是一种常见的算法设计思想&#xff0c;它将一个大问题分解成若干个子问题&#xff0c;递归地解决每个子问题&#xff0c;最后将子问题的解合并起来得到整个问题的解。分治法通常包含三个步骤&#xff1a; 1. Divid…

2023年7月第4周大模型荟萃

2023年7月第4周大模型荟萃 2023.7.31版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 1、Cerebras推出全球最强AI超算 AI芯片初创公司Cerebras Systems和总部位于阿联酋的技术控股集团G42于7月20日宣布&#xff0c;携手打造一个由互联的超…

Docker 阿里云容器镜像服务

阿里云-容器镜像服务ACR 将本地/服务器docker image&#xff08;镜像&#xff09;推送到 阿里云容器镜像服务仓库 1. 在容器镜像服务ACR中创建个人实例 2. 进入个人实例 > 命名空间 创建命名空间 3. 进入个人实例 > 镜像仓库 创建镜像仓库 4. 进入镜像仓库 > 基本信…

CHI中的error处理

Error Handling Error types 包含两种sub-packet级别的error, 和两种packe级别的error; Packet level error Data Error, DERR □ 访问的地址是正确的&#xff0c;但是访问的数据有错误&#xff1b;通常是在数据崩溃的时候使用&#xff0c;例如ECC&#xf…

汽车销售企业消费税,增值税高怎么合理解决?

《税筹顾问》专注于园区招商、企业税务筹划&#xff0c;合理合规助力企业节税&#xff01; 汽车行业一直处于炙手可热的阶段&#xff0c;这是因为个人或者家庭用车的需求在不断攀升&#xff0c;同时随着新能源的技术进一步应用到汽车领域&#xff0c;一度实现了汽车销量的翻倍。…

Java读取及生成pb文件并转换jsonString

Java读取及生成pb文件并转换jsonString 1. 效果图2. 原理2.1 Protocol Buffers是什么2.2 支持的语言2.3 根据.proto生成.java2.4 初始化及构建pb&#xff0c;读取&#xff0c;转jsonString 3. 源码3.1 address.proto3.2 PbParseUtil.java 参考 读取pb及生成pb文件pb文件转换jso…

【Unity细节】关于NotImplementedException: The method or operation is not implemented

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity细节和bug ⭐关于NotImplementedException: The method or operation is not implemented.⭐…

中药配方煎药-亿发智能中药汤剂煎煮系统,智慧中药房的数字化升级

随着中药的普及&#xff0c;在治病、养生等方面都发挥这积极作用&#xff0c;但中药煎煮过程繁琐&#xff0c;如果有所差错将会影响药品的药性。为了满足当今用户对中药的需求&#xff0c;增强生产效率和业务水平&#xff0c;亿发中药煎配智能管理系统应运而生&#xff0c;为用…

机器学习深度学习——多层感知机的从零开始实现

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——多层感知机 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有所帮助 为…

Linux Day03

一、基础命令(在Linux Day02基础上补充) 1.10 find find 搜索路径 -name 文件名 按文件名字搜索 find 搜索路径 -cmin -n 搜索过去n分钟内修改的文件 find 搜索路径 -ctime -n搜索过去n分钟内修改的文件 1&#xff09;按文件名字 2&#xff09;按时间 1.11 grep 在文件中过…

【Ajax】笔记-同源策略

同源策略(Same-Origin Policy)&#xff0c;是浏览器的一种安全策略 同源&#xff08;即url相同&#xff09;&#xff1a;协议、域名、端口号 必须完全相同。&#xff08;请求是来自同一个服务&#xff09; 跨域&#xff1a;违背了同源策略&#xff0c;即跨域。 ajax请求是遵循…

软件测试面试题——接口自动化测试怎么做?

面试过程中&#xff0c;也问了该问题&#xff0c;以下是自己的回答&#xff1a; 接口自动化测试&#xff0c;之前做过&#xff0c;第一个版本是用jmeter 做的&#xff0c;1 主要是将P0级别的功能接口梳理出来&#xff0c;根据业务流抓包获取相关接口&#xff0c;并在jmeter中跑…

BUU CODE REVIEW 1

BUU CODE REVIEW 1 考点&#xff1a;PHP变量引用 源码直接给了 <?phphighlight_file(__FILE__);class BUU {public $correct "";public $input "";public function __destruct() {try {$this->correct base64_encode(uniqid());if($this->c…

回归预测 | MATLAB实现SO-CNN-BiLSTM蛇群算法优化卷积双向长短期记忆神经网络多输入单输出回归预测

回归预测 | MATLAB实现SO-CNN-BiLSTM蛇群算法优化卷积双向长短期记忆神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现SO-CNN-BiLSTM蛇群算法优化卷积双向长短期记忆神经网络多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实…