Navigation Timing API 是一个浏览器提供的接口,用于获取有关网页加载和性能的详细时间信息。
它通过 window.performance 对象提供了一系列的属性和方法,帮助开发者精确测量网页加载过程中各个关键阶段的时间
Navigation Timing 的处理模型
Level 1 的规范在W3C的议程上,它已经功成身退,让位给了精度更高,功能更强大,层次更分明的 Level 2,此文也仅讨论Level 2,对Level 1 感兴趣的可以自行查阅
指标解读
| 属性 | 介绍 | 备注 |
|---|---|---|
| navigationStart | 表示从上一个文档卸载结束时的时间 / 浏览器开始导航到当前页面的时间 | 如果没有上一个文档,这个值和fetchStart相等 |
| unloadEventStart | 表示前一个网页开始执行unload事件的时间 | 如果无前一个网页,或者网页不同源,则值为0 |
| unloadEventEnd | 表示前一个网页unload事件执行完毕的时间 | 如果无前一个网页,或者网页不同源,则值为0 |
| redirectStart | 第一个HTTP重定向开始的时间 | 要有跳转,且是同域名内的跳转,否则值为0 |
| redirectEnd | 最后一个HTTP重定向完成的时间 | 要有跳转,且是同域名内的跳转,否则值为0 |
| fetchStart | 浏览器开始获取页面的时间 / 浏览器准备好使用HTTP请求抓取网络获取资源(通常是当前页面的 HTML 文档)的时间 | 发生在真正开始建立网络连接之前,例如在进行 DNS 查找之前,检测本地缓存之前 |
| domainLookupStart / domainLookupEnd | DNS域名查询开始 / 结束的时间 | 如果使用了本地缓存(即无DNS查询) 或使用了持久连接,则与fetchStart的值相等 |
| connectStart | 开始/重新与服务器建立HTTP(TCP)连接的时间 | 如果是持久连接,则与fetchStart值相等; |
| connectEnd | HTTP(TCP)完成建立的时间(完成握手) | ①它表示连接建立完成(同时也是 TCP/TLS 握手结束)的时间; ②如果是持久连接,则与fetchStart值相等; |
| secureConnectionStart | 开始/重新与服务器建立HTTPS(TLS)连接的时间 | ①与之对应的也是 connectEnd; ②如果不是安全连接,则值为0;③使用的话需要兼容:secureConnectionStart / connectStart - connectEnd |
| requestStart | 浏览器开始发送请求的时间 / HTTP请求开始读取真实文档的时间(建立连接完成) | 包括从本地读取缓存 |
| responseStart | HTTP开始响应 / 浏览器收到响应的第一个字节的时间 | 包括从本地读取缓存 |
| responseEnd | 浏览器响应结束 / HTTP响应全部接收完成的时间(获取到最后一个字节) | 包括从本地读取缓存 |
| domLoading | DOM树开始解析的时间 | 此时Document.readyState是(变为?)loading,很多资料都写着,会触发监听document的readystatechange事件的回调,但实际执行的时候并没有触发 |
| domInteractive | DOM树完成解析的时间 | 此时Document.readyState是变为interactive,会触发监听document的readystatechange事件的回调,此时未开始加载资源 |
| domContentLoadedEventStart | DOM解析完毕后,网页内资源开始加载的时间 | 发生在document的DOMContentLoaded事件触发前 |
| domContentLoadedEventEnd | DOM解析完毕后,网页内资源(js阻塞脚本)加载完毕的时间 | 发生在document的DOMContentLoaded事件执行完毕之后 |
| domComplete | DOM加载完成的时间 | DOM 加载完成,包括页面上的所有资源(如图片、样式表、脚本等)都已经加载完毕,并且页面的布局和渲染也已经完成,但不代表所有资源的下载和显示都已完成,此时Document.readyState是变为complete,会触发监听document的readystatechange事件的回调 |
| loadEventStart | 开始加载 load 事件的时间 | 此时会准备开始执行document的load事件的回调,当loadEventStart时间点被触发时,意味着浏览器已经完成了页面及其所有相关资源的加载,页面处于完全可用的状态。 |
| loadEventEnd | load 事件的回调执行完毕的时间 | load事件会在页面上所有的资源(包括图片、音频、视频等)加载完成后触发 |
补充点
Document.readyState
查询了一些资料,有说监听document的readystatechange事件,会在Document.readyState变为loading的时候执行一次回调函数,但实际上,写的demo发现并没触发,于是去查询了Document.readyState这个属性;
1.runoob网
- [www.runoob.com/jsref/prop-…]
- 如下描述,观点是认为readyState是存在uninitialized的
- readyState 属性返回当前文档的状态(载入中……)。
该属性返回以下值:
- uninitialized - 还未开始载入
- loading - 载入中
- interactive - 已加载,文档与用户可以开始交互
- complete - 载入完成
2.developer.mozilla.org
- [developer.mozilla.org/zh-CN/docs/…]
- 描述是认为只有
loading,interactive,complete三个状态
3.w3c
- [www.w3.org/TR/2021/NOT…]
- 仅枚举了
loading,interactive,complete三个状态
enum DocumentReadyState { "loading", "interactive", "complete" };
个人总结
- 结合监听document的
readystatechange事件回调里打印了Document.readyState,控制台并没有输出过loading,所以认为Document.readyState只有3个状态较为可信loading:文档仍在加载中。interactive:文档已被解析,"正在加载"状态结束,但是诸如图像、样式表和框架之类的子资源仍在加载。意味着可以开始访问 DOM 元素,但部分子资源尚未加载完成。DOMContentLoaded事件即将发生。complete:文档和所有子资源已完成加载,表示load事件即将被触发。
资源加载的定义
通常所说的资源加载,DOM 结构的构建是其中的一部分。
资源加载一般涵盖了多种元素,例如 HTML 文档、CSS 样式表、JavaScript 脚本、图片、音频、视频等。
DOM(Document Object Model,文档对象模型)的构建是基于对 HTML 文档的解析。当浏览器获取到 HTML 代码后,会开始解析并构建 DOM 结构。
所以,从广义上讲,DOM 的构建可以被视为资源加载过程中的一个关键环节。但在讨论资源加载时,往往更侧重于外部引入的资源,如 CSS 文件、JS 文件、图像等,而 DOM 构建更多地被看作是页面初始化和渲染流程中的一个内部步骤。
domContentLoadedEventEnd(阻塞脚本)
- 这里提及了是阻塞脚本,原因是因为加载html,遇到
script标签的时候,不同模型有不同的加载方式,如使用defer或async属性的脚本 - 关于
script的加载方式,可以参考:- 《script标签的几种下载执行模型》[juejin.cn/post/739058…]
DOMContentLoaded事件
当以下两个条件都满足时,就会触发 DOMContentLoaded 事件:
-
第一,浏览器已经成功地把 HTML 文档解析完,并构建好了页面的 DOM 结构。简单来说,就是页面的基本框架和内容在代码层面已经搭建好了。
-
第二,那些会阻塞页面加载的脚本(也就是在 HTML 中直接编写,没有添加
defer或者async属性的脚本)已经全部执行完了。
举个例子,如果一个网页的 HTML 中有一段普通的脚本(没有 defer 或 async),浏览器在解析 HTML 遇到这段脚本时,会停下来先执行这段脚本,只有执行完了,才会继续解析后面的 HTML 来构建 DOM。当 DOM 构建完成,并且这种会阻塞的脚本也都执行完了,就会触发 DOMContentLoaded 事件。
load事件
与 DOMContentLoaded 事件不同,DOMContentLoaded 只要求 DOM 构建完成且无阻塞脚本执行完毕,而 load 事件要等待所有资源完全加载。
- 由于需要等待所有资源加载完成,
load事件可能会在页面加载过程中较晚触发。如果希望尽早执行某些操作,可能更适合使用DOMContentLoaded事件。 - 加载并不完全等同于渲染,加载完毕只能说明是资源都下载(加载)完毕了
实际使用
通过减法组合不同的时间节点,可以定位不同阶段的耗时
let times = {};
let t = window.performance.timing;
// 优先使用 navigation v2 https://www.w3.org/TR/navigation-timing-2/
if (typeof window.PerformanceNavigationTiming === 'function') {
try {
var nt2Timing = performance.getEntriesByType('navigation')[0]
if (nt2Timing) {
t = nt2Timing
}
} catch (err) {
}
}
//重定向时间
times.redirectTime = t.redirectEnd - t.redirectStart;
//dns查询耗时
times.dnsTime = t.domainLookupEnd - t.domainLookupStart;
//TTFB 读取页面第一个字节的时间
times.ttfbTime = t.responseStart - t.navigationStart;
//DNS 缓存时间
times.appcacheTime = t.domainLookupStart - t.fetchStart;
//卸载页面的时间
times.unloadTime = t.unloadEventEnd - t.unloadEventStart;
//tcp连接耗时
times.tcpTime = t.connectEnd - t.connectStart;
//request请求耗时
times.reqTime = t.responseEnd - t.responseStart;
//解析dom树耗时
times.analysisTime = t.domComplete - t.domInteractive;
//白屏时间
times.blankTime = (t.domInteractive || t.domLoading) - t.fetchStart;
//domReadyTime
times.domReadyTime = t.domContentLoadedEventEnd - t.fetchStart;
参考资料:《10分钟彻底搞懂前端页面性能监控》[juejin.cn/post/684490…]
本文为个人整理,做了细节补充和额外拓展,如有错误,欢迎指正