skip to content
usubeni fantasy logo Usubeni Fantasy

前端性能优化教程——理解关键路径

/ 19 min read

“关键渲染路径”是指网页在浏览器中开始显示前必须经历的一系列步骤。浏览器要显示网页,不仅需要获取 HTML 文档,还需要所有对显示该文档至关重要的资源。

在之前的HTML 性能基础部分,我们已经讨论了如何把 HTML 文档送达浏览器。本部分将深入探讨,浏览器在接收到 HTML 文档之后,为了显示网页它都做了哪些工作。

渐进式渲染

互联网的分布式特性意味着与先安装后使用的本地应用不同,浏览器无法期待网站自带渲染页面必需的全部资源。因此,浏览器特别设计出能够逐步渲染页面的能力。本地应用分为安装和运行两个阶段,而网页和 Web 应用则将这两个阶段的界线模糊化了。

当浏览器获取到渲染某个页面所需的资源后,通常会立即开始渲染。这就引出了一个问题:何时开始渲染才算恰当?

如果浏览器在仅获取到一些 HTML 内容而尚未加载任何 CSS 或必要的 JavaScript 时就急于渲染,页面将会暂时出现破碎的外观,并且在最终渲染时会有较大的变化。这种体验,与先展示一个空白屏幕直到浏览器加载到足够的资源进行初次渲染并提供更佳用户体验相比,显然是较为糟糕的选择。

另一方面,如果浏览器等待所有资源全部就绪再开始逐步渲染,用户可能需要无谓地等待很长时间;特别是在网页在更早阶段就已足够使用时,这样的等待显得尤为不必要。

为了避免展现一个明显有缺陷的页面,浏览器需要确定它至少应该等待哪些必要资源的加载。同时,浏览器也不应该在展示给用户一些内容之前无谓地延长等待时间。浏览器在进行首次渲染之前的这一系列步骤,被称为关键渲染路径

理解关键渲染路径对于提高网页性能至关重要,它确保你不会不必要地延迟页面的初次渲染。不过,同样重要的是,为了快而从关键渲染路径中移除对首次渲染至关重要的资源。

关键渲染路径

渲染路径包括以下步骤:

  • 根据 HTML 代码构建文档对象模型(DOM)。
  • 根据 CSS 代码构建 CSS 对象模型(CSSOM)。
  • 执行可能会修改 DOM 或 CSSOM 的 JavaScript 代码。
  • 结合 DOM 和 CSSOM,创建渲染树。
  • 对网页进行样式和布局处理,确定各个元素的摆放位置。
  • 在内存中为这些元素生成像素点。
  • 如果这些像素点之间有重叠,就进行像素合成。
  • 把最终生成的像素点绘制到屏幕上。

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

LearnMore

关于这些步骤的更多细节,你可以参考本文及其参考文章。实际上,这个过程可能比你想象的还要复杂。但是,如果你只是想初步了解网页性能,这篇文章提供的概述应该已经足够了。

渲染过程不止一次发生。首次渲染启动了这一过程,但是随着更多影响页面展示效果的资源陆续到位,浏览器会重复执行这一过程——或者至少是部分过程——以此来更新用户眼前所见的内容。所谓的“关键渲染路径”重点关注了初始渲染阶段前面所提到的过程,并且这一路径的实现依赖于那些对渲染至关重要的资源。

关键渲染路径中有什么资源?

在浏览器显示网页之前,它需要先下载一些重要的资源。这些必须先下载的资源包括:

  • 网页的一部分 HTML 代码。
  • 位于 <head> 元素中的阻塞渲染的 CSS。
  • 位于 <head> 元素中的阻塞渲染的 JavaScript。

浏览器处理网页内容时采用的是流式处理方式,这意味着一旦浏览器接收到网页的任何一部分 HTML 代码,它就开始立即处理。基于这个机制,浏览器往往会在还没完全接收到整个页面的 HTML 之前就开始渲染页面。

值得注意的是,在页面的首次渲染阶段,浏览器通常不会等待以下内容完全加载:

  • 整个页面的 HTML 代码。
  • 字体文件。
  • 图片文件。
  • 位于 <head> 标签之外,不会阻塞页面渲染的 JavaScript 代码(比如,放在页面底部的 <script> 标签)。
  • 位于 <head> 标签之外,不会阻塞页面渲染的 CSS 样式表,或者那些媒介查询属性值并不适配当前浏览器视窗大小的 CSS。

在网页加载过程中,浏览器通常会把字体和图片视为稍后填充的内容,这样做不会阻碍网页最开始的渲染。但这种做法可能会导致一个问题:在等待字体加载或图片出现之前,网页初次渲染会出现空白区域,文字内容也可能被隐藏。更严重的情况是,如果没有为某些类型的内容预留足够的空间——尤其是当网页 HTML 中没有指定图片的具体尺寸时,这些内容加载进来后会导致网页布局发生改变。这种对用户体验产生影响的情况,可以通过累积布局偏移(CLS)这个指标来进行衡量。

<head> 元素对于处理页面的关键渲染过程至关重要。因此,下一节将对此进行深入讲解。优化 <head> 元素内的内容是提升网页性能的一个核心步骤。简单来说,目前你只需要知道 <head> 元素含有页面及其资源的元数据,这些元数据对用户是不可见的。用户能看到的内容都在 <body> 元素中,而 <body> 元素位于 <head> 元素之后。浏览器在展示任何内容前,需要获取到内容本身及其渲染方式的相关元数据。

不过,并不是所有在 <head> 元素中引用的资源都对页面的首次渲染至关重要,浏览器仅需等待那些关键的资源。要识别出哪些资源是关键渲染路径的一部分,你必须了解哪些 CSS 和 JavaScript 会阻塞渲染或阻塞解析。

渲染阻塞资源

在网页加载过程中,有些资源被认为特别重要,以至于浏览器会暂停其他内容的渲染,直到这些资源被完全处理完毕。CSS 就是这类资源的一个典型例子。

当浏览器遇到 CSS 代码时——不管是嵌入在 <style> 标签内的内联 CSS,还是通过 <link rel=stylesheet href="..."> 标签引用的外部 CSS 文件——它都会先停下来,不继续渲染页面上的其他内容,直到这些 CSS 资源被下载并处理完成。

NOTE

尽管 CSS 默认会阻止网页渲染,但我们可以通过调整 <link> 标签中的 media 属性,设定一个与当前环境不符的值(如:<link rel=stylesheet href="..." media=print>),来改变这一行为,使 CSS 成为非阻塞渲染资源。这种方法曾被用来使那些不是立即需要的 CSS 能够以一种不阻塞页面渲染的方式被加载。

一个资源阻塞页面渲染,并不意味着它会阻止浏览器进行其他操作。浏览器力求效率最大化,因此在需要下载 CSS 资源时,虽会暂停页面的渲染过程,但浏览器仍会继续解析剩余的 HTML 内容,并同时寻找其他可以执行的任务。

在过去,CSS 这样的渲染阻塞资源一旦被检测到,就会阻塞页面的全部渲染过程。换句话说,CSS 是否会阻塞渲染取决于浏览器是否已经发现了它。某些浏览器(起初是 Firefox,现在 Chrome 也是如此)仅阻塞在渲染阻塞资源之下的内容渲染。换言之,在处理关键渲染路径时,我们特别关注位于 <head> 标签中的渲染阻塞资源,因为它们实质上阻塞了整个页面的渲染。

Chrome 105 最近引入了一个新属性 blocking=render。开发者现在可以用这个属性来指定 <link><script><style> 元素,在这些元素完成加载和处理之前,不允许页面渲染这部分内容,但这不会阻止页面的其余部分继续加载和解析。

解析器阻塞资源

解析器阻塞资源会阻止浏览器同时执行其他任务(这些任务需要继续对 HTML 进行解析)。默认情况下,JavaScript 就是解析器阻塞资源(除非它被明确标记为异步延迟加载),因为它执行时可能会修改 DOM 或 CSSOM。因此,在浏览器完全理解了某个 JavaScript 对网页 HTML 的全部影响之前,它无法开始处理其他资源。这意味着同步 JavaScript 会阻止网页的解析过程。

解析器阻塞资源也会影响网页的渲染。因为在解析器完全处理完一个阻塞资源之前,它无法处理和渲染后续的内容。尽管在等待过程中浏览器能够渲染它已经接收到的 HTML 内容,但从关键渲染路径的角度来看,<head> 标签中的解析器阻塞资源实际上导致了页面上所有内容的渲染都被阻塞了。

阻止解析器工作会严重拖慢网页性能,这种影响远比简单地延迟页面显示要大得多。为了减少这种不良影响,浏览器采用了一个叫做预加载扫描器的辅助 HTML 解析器。当主解析器停滞不前时,这个辅助解析器会提前下载接下来的网络资源。尽管这种方式不及直接解析 HTML 那样高效,但它至少可以确保浏览器的网络活动不会因为解析器的停滞而全面停摆,未来再被阻塞的可能性就会降低。

识别阻塞资源

很多性能评估工具都能够识别出会阻塞渲染和解析的资源。例如,WebPageTest 就会在阻塞渲染的资源的 URL 左侧加上一个橙色圆形来进行标记:

所有会阻碍页面渲染开始的资源,都必须先完成下载和处理。在性能分析图上,我们通过一条实心的深绿色线来标记这一阶段。

Lighthouse 工具也能识别这些阻碍渲染的资源,但它的提示更为精巧,仅在这些资源真正导致页面渲染延迟时才会发出警告。这种方法有助于减少误判,尤其是在你已经在努力减少渲染阻塞的情况下。使用 Lighthouse 对之前 WebPageTest 展示的同一个页面链接进行测试时,它只将其中一个样式表标记为了渲染阻塞资源。

优化关键渲染路径

优化关键渲染路径的目标是减少加载 HTML 的时间(这一过程通过首字节到达时间(TTFB)来衡量),正如前面的章节所解释的那样,并减轻渲染阻塞资源的影响。这些主题将在接下来的章节中详细讨论。

关键内容渲染路径

长时间以来,我们通常将关键渲染路径的焦点放在了页面的首次加载上。然而,随着越来越多注重用户体验的网页性能指标的出现,人们开始思考我们是否应该只关注于页面的首次绘制,还是应该重视那些更有内容、更能吸引用户的绘制。

一种新的看法是,我们或许应该将注意力转移到最大内容绘制(LCP)首次内容绘制(FCP)所需的时间上,将其视为一个更注重内容的渲染路径(有些人可能会称之为关键路径【译注:这里的关键是“key”,前面的关键是“critical”】)。这意味着,在这一过程中,我们可能会涉及到一些传统上不被视为阻塞的资源,但这些资源对于实现丰富的内容绘制来说是必不可少的。

不管你怎样精确地定义“关键”,了解是什么因素在任何初始页面展示及其核心内容的背后发挥作用是非常重要的。所谓的首次绘制是指向用户展示任何内容的第一次机会。在理想状态下,这应当是一些有实际意义的内容——而不仅仅是背景颜色之类的元素。即便展示的内容没有具体的信息,能够向用户呈现一些东西本身就是有价值的,这支持了按照传统定义去衡量关键渲染路径的观点。同时,衡量主要内容呈现给用户的时刻也是十分重要的。

识别内容渲染路径

很多工具能够识别出最大内容绘制(LCP)元素及其渲染时刻。除了 LCP 元素本身,Lighthouse 还提供了识别 LCP 阶段及各阶段所需时间的功能,帮助你明确优化工作的重点应该放在哪里:

对于结构更为复杂的网站,Lighthouse 还会在一个独立的审查项目中特别标出关键请求之间的依赖链

Lighthouse 这项审查检查了所有被赋予高优先级的加载资源。这不仅包括网络字体,还有 Chrome 判定为高优先级的其他内容,哪怕这些资源实际上并不会阻碍页面的渲染。

文章信息

评论组件加载中……