导航和资源时间

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

以下通用性能计时已被弃用,取而代之的是 Performance Entry API,后者提供了标记和测量导航和资源加载过程中的时间。虽然已弃用,但它们在所有浏览器中都受支持。

性能计时

performanceTiming API 是一个用于测量请求页面加载性能的 JavaScript 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;

首字节时间

首字节时间 (Time to First Byte)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;

Performance Entry API

上述通用性能计时已弃用但完全受支持。我们现在有 Performance Entry 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、可访问性和渲染树,最终渲染页面。一旦用户代理停止解析文档,用户代理会将文档就绪状态设置为交互式。如果有需要解析的延迟脚本,它将进行解析,然后触发 DOMContentLoaded 事件,之后就绪状态设置为完成。文档现在可以处理加载后任务,之后文档被标记为完全加载。

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

performance.getEntriesByType('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;

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

压缩

要获得压缩节省百分比,我们将 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 事件前的精确时间值来测量的。将其保持在 50 毫秒或更快有助于确保响应式用户界面。

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

持续时间

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

PerformanceNavigationTiming 接口还提供了关于您正在测量的导航类型的信息,返回 navigatereloadback_forward

资源计时

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

Graphic of Resource Timing timestamps

每个资源需要关注的主要事项。

另见