了解关键路径

关键渲染路径是指网页开始在浏览器中渲染之前所涉及的步骤。要渲染网页,浏览器需要 HTML 文档本身以及渲染该文档所需的所有关键资源。

之前的通用 HTML 性能注意事项模块介绍了如何将 HTML 文档发送到浏览器。但是,在本模块中,我们将更多地关注浏览器在下载 HTML 文档之后为了渲染页面所做的工作。

渐进式渲染

Web 的本质是分布式的。与使用前需要安装的原生应用不同,浏览器不能依赖网站拥有渲染页面所需的所有资源。因此,浏览器非常擅长渐进式渲染页面。原生应用通常具有安装阶段,然后是运行阶段。但是,对于网页和 Web 应用,这两个阶段之间的界限不太明显,并且浏览器在设计时就特别考虑到了这一点。

一旦浏览器拥有渲染页面的资源,它通常就会开始渲染。因此,选择就变成了何时渲染:何时渲染太早?

如果浏览器在仅具有一些 HTML 时(但在具有任何 CSS 或必要的 JavaScript 之前)尽快渲染,则页面会瞬间看起来损坏,并且对于最终渲染会发生很大变化。与最初呈现一段时间的空白屏幕相比,这是一种更糟糕的体验,直到浏览器拥有更多初始渲染所需的资源,从而提供更好的用户体验。

另一方面,如果浏览器等待所有资源都可用,而不是进行任何顺序渲染,那么用户将等待很长时间;如果页面在更早的时间点可用,则通常是不必要的。

浏览器需要知道它应该等待的最少资源数量,以避免呈现明显的损坏体验。另一方面,浏览器也不应在向用户呈现某些内容之前等待不必要的时间。浏览器在执行初始渲染之前所采取的步骤序列称为关键渲染路径

了解关键渲染路径可以帮助提高 Web 性能,确保您不会不必要地阻止初始页面渲染。但是,与此同时,重要的是也要避免过早地进行渲染,方法是从关键渲染路径中删除初始渲染的必要资源。

(关键)渲染路径

渲染路径包括以下步骤

  • 从 HTML 构建文档对象模型 (DOM)。
  • 从 CSS 构建 CSS 对象模型 (CSSOM)。
  • 应用任何更改 DOM 或 CSSOM 的 JavaScript。
  • 从 DOM 和 CSSOM 构建渲染树。
  • 对页面执行样式和布局操作,以查看哪些元素适合放在哪里。
  • 在内存中绘制元素的像素。
  • 如果任何像素重叠,则合成像素。
  • 将所有生成的像素物理绘制到屏幕上。
The rendering process from HTML and CSS to the displaying of pixels.
渲染过程,如前一个列表所述。

只有在完成所有这些步骤之后,用户才能在屏幕上看到内容。

此渲染过程会多次发生。初始渲染会调用此过程,但是随着更多影响页面渲染的资源变得可用,浏览器将重新运行此过程(或仅运行其中的一部分)以更新用户看到的内容。关键渲染路径侧重于先前概述的初始渲染过程,并取决于其必要的关键资源。

哪些资源位于关键渲染路径上?

浏览器需要等待一些关键资源下载完成后才能完成初始渲染。这些资源包括

  • HTML 的一部分。
  • <head> 元素中的渲染阻塞 CSS。
  • <head> 元素中的渲染阻塞 JavaScript。

关键点是浏览器以流式方式处理 HTML。浏览器一旦获得页面的任何一部分 HTML,就会开始处理它。然后,浏览器可以(并且经常会)决定在收到页面的其余 HTML 之前就对其进行渲染。

重要的是,对于初始渲染,浏览器通常会等待

  • 所有 HTML。
  • 字体。
  • 图片。
  • <head> 元素之外的非渲染阻塞 JavaScript(例如,放置在 HTML 末尾的 <script> 元素)。
  • <head> 元素之外的非渲染阻塞 CSS,或 media 属性值不适用于当前视口的 CSS。

字体和图片通常被浏览器视为在后续页面重新渲染期间填充的内容,因此它们不需要阻止初始渲染。但是,这可能意味着初始渲染中会留下空白区域,同时文本被隐藏以等待字体,或者直到图片可用。更糟糕的是,当没有为某些类型的内容(特别是当 HTML 中未提供图片尺寸时)保留足够的空间时,页面的布局可能会在该内容稍后加载时发生偏移。用户体验的这一方面由累积布局偏移 (CLS)指标衡量。

<head> 元素是处理关键渲染路径的关键。以至于下一节将详细介绍它。优化 <head> 元素的内容是 Web 性能的关键方面。但是,要了解现在的关键渲染路径,您只需要知道 <head> 元素包含有关页面及其资源的元数据,但没有用户可以看到的实际内容。可见内容包含在 <body> 元素中,该元素位于 <head> 元素之后。在浏览器可以渲染任何内容之前,它需要渲染的内容以及有关如何渲染它的元数据。

但是,并非 <head> 元素中引用的所有资源对于初始页面渲染都是绝对必要的,因此浏览器仅等待那些必要的资源。要确定哪些资源在关键渲染路径中,您需要了解渲染阻塞和解析器阻塞 CSS 和 JavaScript。

渲染阻塞资源

某些资源被认为非常关键,以至于浏览器会暂停页面渲染,直到处理完这些资源。默认情况下,CSS 属于此类。

当浏览器看到 CSS 时(无论是 <style> 元素中的内联 CSS,还是由 <link rel=stylesheet href="..."> 元素指定的外部引用资源),浏览器都会避免渲染任何更多内容,直到它完成下载和处理该 CSS。

仅仅因为资源阻止渲染并不一定意味着它会阻止浏览器执行任何其他操作。浏览器会尽可能高效,因此当浏览器看到需要下载 CSS 资源时,它会请求它并暂停渲染,但仍会继续处理其余 HTML,并寻找在此期间要做的其他工作。

渲染阻塞资源(如 CSS)过去在被发现时会阻止页面的所有渲染。这意味着某些 CSS 是否是渲染阻塞的取决于浏览器是否已发现它。一些浏览器(最初是 Firefox,现在Chrome 也是如此)仅阻止渲染渲染阻塞资源下方的内容。这意味着对于关键渲染阻塞路径,我们通常对 <head> 中的渲染阻塞资源感兴趣,因为它们有效地阻止了整个页面的渲染。

最近的一项创新是 blocking=render 属性已添加到 Chrome 105。这允许开发人员显式地将 <link><script><style> 元素标记为渲染阻塞,直到元素被处理完毕,但仍然允许解析器同时继续处理文档。

解析器阻塞资源

解析器阻塞资源是那些通过继续解析 HTML 来阻止浏览器寻找其他工作来完成的资源。默认情况下,JavaScript 是解析器阻塞的(除非专门标记为异步延迟),因为 JavaScript 可以在执行时更改 DOM 或 CSSOM。因此,浏览器无法继续处理其他资源,直到它知道请求的 JavaScript 对页面 HTML 的全部影响。因此,同步 JavaScript 会阻止解析器。

解析器阻塞资源实际上也是渲染阻塞的。由于解析器在完全处理完解析阻塞资源之前无法继续进行,因此它无法访问和渲染其后的内容。浏览器可以在等待时渲染到目前为止收到的任何 HTML,但就关键渲染路径而言,<head> 中的任何解析器阻塞资源实际上都意味着所有页面内容都被阻止渲染。

阻止解析器可能会产生巨大的性能成本,远远超过仅阻止渲染。因此,浏览器将尝试通过使用辅助 HTML 解析器(称为预加载扫描器)来减少此成本,以便在主 HTML 解析器被阻止时下载即将到来的资源。虽然不如实际解析 HTML 那么好,但它至少允许浏览器中的网络功能在阻止的解析器之前工作,这意味着将来不太可能再次被阻止。

识别阻塞资源

许多性能审核工具都可以识别渲染和解析器阻塞资源。WebPageTest 使用资源 URL 左侧的橙色圆圈标记渲染阻塞资源

Network waterfall diagram generated by WebPageTest. The parser-blocking resources are noted by a orange circle to the left of the resource's URL, and the start render time is identified by a solid dark green line.
由 WebPageTest 生成的网络瀑布图。

所有渲染阻塞资源都需要在渲染开始之前下载和处理,这在瀑布图中用实心深绿色线条表示。

Lighthouse 也突出显示渲染阻塞资源,但方式更微妙,并且仅在资源实际延迟页面渲染时才突出显示。这有助于避免误报,否则您将最大限度地减少渲染阻塞。通过 Lighthouse 运行与前面的 WebPageTest 图形相同的页面 URL,仅将其中一个样式表识别为渲染阻塞资源。

Lighthouse's audit for eliminating render-blocking resources. The audit shows the resource(s) that block rendering, and the amount of time they block for.
Lighthouse 消除渲染阻塞资源的审核。

优化关键渲染路径

优化关键渲染路径包括减少接收 HTML 的时间(由先前的模块中详细介绍的首字节时间 (TTFB) 指标表示)以及减少渲染阻塞资源的影响。接下来的模块将研究这些概念。

关键内容渲染路径

长期以来,关键渲染路径一直关注初始渲染。但是,已经出现了更多以用户为中心的 Web 性能指标,这让人们开始质疑关键渲染路径的终点应该是第一次绘制还是之后的内容丰富的绘制之一。

另一种观点是,转而关注最大内容渲染 (LCP)之前的时间,甚至首次内容渲染 (FCP),作为内容丰富的渲染路径的一部分(或其他人可能称之为关键路径)。在这种情况下,您可能需要包含不一定是阻塞的资源(正如关键渲染路径的典型定义那样),但对于渲染内容丰富的绘制是必要的资源。

无论您对“关键”的准确定义是什么,了解是什么阻碍了任何初始渲染和您的关键内容都很重要。首次绘制衡量了为用户渲染任何内容的第一个可能的机会。理想情况下,这应该是有意义的内容,例如,不是背景颜色,但即使它不是内容丰富的,向用户呈现某些内容仍然有价值,这是衡量传统定义的关键渲染路径的论据。与此同时,衡量主要内容何时呈现给用户也很有价值。

识别内容丰富的渲染路径

许多工具都可以识别 LCP 元素及其渲染时间。除了LCP 元素之外,Lighthouse 还可以帮助识别LCP 阶段和每个阶段花费的时间,以帮助您了解最好在何处集中优化工作

Lighthouse's LCP audit, which shows a page's LCP element and the amount of time it has spent in phases such as its TTFB, load delay, load time, and render delay.
Lighthouse 的 LCP 审核。

对于更复杂的站点,Lighthouse 还在单独的审核中突出显示关键请求链

Lighthouse's critical request chain diagram, which shows which critical resources are nested beneath other critical resources, as well as the total latency involved in the critical request chain.
Lighthouse 的关键请求链图。

此 Lighthouse 审核观察以高优先级加载的所有资源,因此它包括 Web 字体和其他 Chrome 设置为高优先级资源的内容,即使它实际上不是渲染阻塞的。

测试您的知识

关键渲染路径指的是什么?

完全渲染页面所需的最小资源量。
请重试。
执行初始页面渲染所需的最小资源量。
正确!

关键渲染路径中涉及哪些资源?

HTML 的一部分。
正确!
<head> 元素中的渲染阻塞 CSS。
正确!
<head> 元素中的渲染阻塞 JavaScript。
正确!

为什么渲染阻塞是页面渲染的必要组成部分?

防止页面最初以不可用或明显损坏的状态渲染。
正确!
防止用户在页面完全渲染之前看到页面。
请重试。

为什么 JavaScript 会阻止 HTML 解析器(假设 deferasyncmodule 属性未在 <script> 元素上指定)?

如果没有至少其中一个属性,则 <script> 是解析器阻塞和渲染阻塞的。
正确!
所有 JavaScript 都是解析器阻塞的,无论这些属性如何。
请重试。
同步 JavaScript 必须在解析器到达它时执行,因为它可能会更改 DOM。
正确!

下一步:优化资源加载

本模块介绍了浏览器如何渲染网页的一些理论,特别是完成页面初始渲染所必需的内容。下一个模块将介绍如何通过学习如何优化资源加载来优化此渲染路径。