导航和资源计时

导航计时是衡量浏览器文档导航事件的指标。资源计时是关于应用程序资源加载的详细网络计时测量。两者都提供相同的只读属性,但导航计时测量主文档的计时,而资源计时提供所有由该主文档调用的资产或资源以及资源请求的资源的计时。

以下常规性能计时已被弃用,取而代之的是性能条目 API,它可以用于标记和测量导航和资源加载过程中的时间。尽管已弃用,但它们在所有浏览器中都受支持。

性能计时

用于测量请求页面的加载性能的performanceTiming API已弃用,但在所有浏览器中都受支持。它已被performanceNavigationTiming API 替代。

性能计时 API 以毫秒 (ms) 为单位提供只读时间,描述在页面加载过程中的每个点何时到达。如下面的图像所示,导航过程从navigationStartunloadEventStartunloadEventEndredirectStartredirectEndfetchStartdomainLookupStartdomainLookupEndconnectStartconnectEndsecureConnectionStartrequestStartresponseStartresponseEnddomLoadingdomInteractivedomContentLoadedEventStartdomContentLoadedEventEnddomCompleteloadEventStartloadEventEnd

Navigation Timing event metrics

使用上述指标和一些数学运算,我们可以计算许多重要的指标,例如首字节时间、页面加载时间、DNS 查找以及连接是否安全。

为了帮助测量完成所有步骤所需的时间,Performance Timing API 提供了导航计时的只读测量。要查看和捕获我们应用程序的计时,我们输入

js
let time = window.performance.timing;

然后我们可以使用结果来衡量我们的应用程序的性能。

entering window.performance.timing in the console lists all the timings in the PerformanceNavigationTiming interface

顺序为

性能计时 细节
navigationStart 当对先前文档(在同一浏览上下文中)的卸载提示终止时。如果没有先前的文档,则此值将与PerformanceTiming.fetchStart相同。
secureConnectionStart 当安全连接握手开始时。如果没有请求此类连接,则返回0
redirectStart 当第一个 HTTP 重定向开始时。如果没有重定向,或者重定向之一不是同一来源,则返回的值为0
redirectEnd

当最后一个 HTTP 重定向完成时,即当接收到 HTTP 响应的最后一个字节时。如果没有重定向,或者重定向之一不是同一来源,则返回的值为0

connectEnd 当连接打开网络时。如果传输层报告错误并且连接建立重新开始,则给出最后一次连接建立结束时间。如果使用持久连接,则该值将与PerformanceTiming.fetchStart相同。当所有安全连接握手或 SOCKS 身份验证终止时,连接被视为已打开。
connectStart 当向网络发送打开连接的请求时。如果传输层报告错误并且连接建立重新开始,则给出最后一次连接建立开始时间。如果使用持久连接,则该值将与PerformanceTiming.fetchStart相同。
domainLookupEnd 当域名查找完成时。如果使用持久连接,或者信息存储在缓存或本地资源中,则该值将与PerformanceTiming.fetchStart相同。
domainLookupStart 当域名查找开始时。如果使用持久连接,或者信息存储在缓存或本地资源中,则该值将与PerformanceTiming.fetchStart相同。
fetchStart 当浏览器准备好使用 HTTP 请求获取文档时。此时刻检查任何应用程序缓存之前
requestStart 当浏览器发送请求以从服务器或缓存中获取实际文档时。如果传输层在请求开始后失败并且连接重新打开,则此属性将设置为对应于新请求的时间。
responseStart 当浏览器从服务器、缓存或本地资源接收到响应的第一个字节时。
responseEnd 当浏览器接收到响应的最后一个字节时,或者如果连接先关闭,则从服务器、缓存或本地资源接收到响应的最后一个字节时。
domLoading 当解析器开始工作时,即其Document.readyState更改为'loading'并且抛出相应的readystatechange事件时。
unloadEventStart 当抛出unload事件时,指示窗口中先前文档开始卸载的时间。如果没有先前的文档,或者先前的文档或所需重定向之一不是同一来源,则返回的值为0
unloadEventEnd unload事件处理程序完成时。如果没有先前的文档,或者先前的文档或所需重定向之一不是同一来源,则返回的值为0
domInteractive 当解析器完成对主文档的工作时,即其Document.readyState更改为'interactive'并且抛出相应的readystatechange事件时。
domContentLoadedEventStart 在解析器发送DOMContentLoaded事件之前,即在解析后需要执行的所有脚本都已执行之后。
domContentLoadedEventEnd 在所有需要尽快执行的脚本(无论顺序如何)都已执行之后。
domComplete 当解析器完成对主文档的工作时,即其Document.readyState更改为'complete'并且抛出相应的readystatechange事件时。
loadEventStart 当为当前文档发送load事件时。如果尚未发送此事件,则返回0.
loadEventEnd load事件处理程序终止时,即加载事件完成时。如果尚未发送此事件或尚未完成,则返回0.

计算计时

我们可以使用这些值来衡量感兴趣的特定计时

js
const dns = time.domainLookupEnd - time.domainLookupStart;
const tcp = time.connectEnd - time.connectStart;
const tls = time.requestStart - time.secureConnectionStart;

首字节时间

首字节时间是在navigationStart(导航开始)和responseStart(接收到响应数据的第一个字节时)之间的时间,在performanceTiming API 中可用

js
const ttfb = time.responseStart - time.navigationStart;

页面加载时间

页面加载时间是在navigationStart和为当前文档发送加载事件的开始之间的时间。它们仅在 performanceTiming API 中可用。

js
let pageloadtime = time.loadEventStart - time.navigationStart;

DNS 查找时间

DNS 查找时间是在domainLookupStartdomainLookupEnd之间的时间。这些都可以在performanceTimingperformanceNavigationTiming API 中使用。

js
const dns = time.domainLookupEnd - time.domainLookupStart;

TCP

完成TCP握手所需的时间是在连接开始和连接结束之间的时间

js
const tcp = time.connectEnd - time.connectStart;

TLS 协商

secureConnectionStart如果不可用则为undefined,如果未使用HTTPS则为0,或者如果可用且已使用则为时间戳。换句话说,如果使用了安全连接,则secureConnectionStart将为真值,并且secureConnectionStartrequestStart之间的时间将大于 0。

js
const tls = time.requestStart - time.secureConnectionStart;

性能条目 API

上述一般性能计时已弃用,但完全受支持。我们现在有了性能条目 API,它可以用于标记和测量导航和资源加载过程中的时间。您还可以创建标记

js
performance.getEntriesByType("navigation").forEach((navigation) => {
  console.dir(navigation);
});

performance.getEntriesByType("resource").forEach((resource) => {
  console.dir(resource);
});

performance.getEntriesByType("mark").forEach((mark) => {
  console.dir(mark);
});

performance.getEntriesByType("measure").forEach((measure) => {
  console.dir(measure);
});

performance.getEntriesByType("paint").forEach((paint) => {
  console.dir(paint);
});

performance.getEntriesByType("frame").forEach((frame) => {
  console.dir(frame);
});

在支持的浏览器中,您可以使用performance.getEntriesByType('paint')查询first-paintfirst-contentful-paint的测量结果。我们使用performance.getEntriesByType('navigation')performance.getEntriesByType('resource')分别查询导航和资源的计时信息。

当用户请求一个网站或应用程序时,为了填充浏览器,用户代理会经历一系列步骤,包括DNS查找、TCP握手和TLS协商,然后用户代理才会发出实际请求,服务器返回请求的资源。浏览器随后解析接收到的内容,构建DOM、CSSOM、可访问性和渲染树,最终渲染页面。一旦用户代理停止解析文档,用户代理就会将文档就绪状态设置为interactive。如果存在需要解析的延迟脚本,它将执行解析,然后触发DOMContentLoaded事件,之后就绪状态将设置为complete。文档现在可以处理加载后的任务,在此之后,文档被标记为完全加载。

js
const navigationTimings = performance.getEntriesByType("navigation");

performance.getEntriesByType('navigation')返回一个包含navigation类型的PerformanceEntry对象的数组。

The results of when performance.getEntriesByType('navigation'); is entered into the console for this document

从这些计时信息中可以获得很多有用的信息。在上图中,我们通过name属性可以看到正在计时的文件是当前文档。在接下来的解释中,我们使用以下变量

js
const timing = performance.getEntriesByType("navigation")[0];

协议

我们可以通过查询来检查使用的协议

js
const protocol = timing.nextHopProtocol;

它返回用于获取资源的网络协议:在本例中,h2表示http/2

压缩

要获取压缩节省的百分比,我们将transferSize除以decodedBodySize,然后从100%中减去结果。我们看到节省了超过74%。

js
const compressionSavings = 1 - timing.transferSize / timing.decodedBodySize;

我们本可以使用

js
const compressionSavings = 1 - timing.encodedBodySize / timing.decodedBodySize;

但使用transferSize包含了开销字节。

为了比较,我们可以查看网络选项卡,发现我们传输了22.04KB,未压缩的文件大小为87.24KB。

View of the bytes transferred and the size via the network tab

如果我们用这些数字进行计算,会得到相同的结果:1 - (22.04 / 87.24) = 0.747。导航计时为我们提供了一种以编程方式检查传输大小和带宽节省的方法。

请注意,这是单个文档的大小:仅针对此资源,不包括所有资源的总和。但是,持续时间、加载事件和与DOM相关的计时与整个导航有关,而不是单个资源。客户端Web应用程序可能看起来比此应用程序快,传输大小低于10000,解码正文大小低于30000,但这并不意味着JavaScript、CSS或媒体资源没有增加冗余。检查压缩率很重要,但也要检查持续时间以及DOMContentLoaded事件结束到DOM完成之间的时间,因为在主线程上长时间运行JavaScript会导致用户界面无响应。

请求时间

API没有提供您可能希望的每个测量值。例如,请求花费了多长时间?我们可以使用我们拥有的测量值来获得答案。

要测量响应时间,请从响应开始时间减去请求开始时间。请求开始是用户代理开始从服务器或相关应用程序缓存或本地资源请求资源之前的时刻。响应开始是用户代理的HTTP解析器从相关应用程序缓存或本地资源或服务器接收响应的第一个字节之后的时刻,这发生在接收和处理请求之后。

js
const request = timing.responseStart - timing.requestStart;

加载事件持续时间

通过从当前文档的加载事件完成时的时间减去当前文档的加载事件触发之前的时刻的时间戳,可以测量加载事件的持续时间。

js
const load = timing.loadEventEnd - timing.loadEventStart;

DOMContentLoaded事件

DOMContentLoaded事件持续时间通过从用户代理触发DOMContentLoaded事件后的时刻的时间值减去触发DOMContentLoaded事件之前的时刻的时间值来测量。将此时间保持在50毫秒或更短有助于确保用户界面响应迅速。

js
const DOMContentLoaded =
  timing.domContentLoadedEventEnd - timing.domContentLoadedEventStart;

持续时间

我们获得了持续时间。持续时间是PerformanceNavigationTiming.loadEventEndPerformanceEntry.startTime属性之间的差值。

PerformanceNavigationTiming接口还提供有关正在测量的导航类型的信息,返回navigatereloadback_forwardprerender

资源计时

导航计时用于测量主页面(通常是请求所有其他资源的HTML文件)的性能,而资源计时则用于测量单个资源(主页面调用的资源以及这些资源请求的任何资源)的计时。许多测量值是相似的:每个域名都会执行一次DNS查找、TCP握手和安全连接启动。

Graphic of Resource Timing timestamps

每个资源的主要关注点。

另请参阅