关键渲染路径
关键渲染路径是浏览器将 HTML、CSS 和 JavaScript 转换为屏幕像素所经历的步骤序列。优化关键渲染路径可以提升渲染性能。关键渲染路径包括 文档对象模型 (DOM)、CSS 对象模型 (CSSOM)、渲染树和布局。
在解析 HTML 时,会创建文档对象模型。HTML 可能请求 JavaScript,而 JavaScript 又可能修改 DOM。HTML 包含或请求样式,然后构建 CSS 对象模型。浏览器引擎将两者结合起来创建渲染树。布局决定了页面上所有元素的尺寸和位置。一旦确定了布局,像素就会被绘制到屏幕上。
优化关键渲染路径可缩短首次渲染时间。理解和优化关键渲染路径对于确保每秒 60 帧的重排和重绘,确保高性能的用户交互,以及避免卡顿至关重要。
理解 CRP
Web 性能包括服务器请求和响应、加载、脚本、渲染、布局以及像素绘制到屏幕的整个过程。
对网页或应用程序的请求始于 HTTP 请求。服务器发送包含 HTML 的响应。然后浏览器开始解析 HTML,将接收到的字节转换为 DOM 树。每次浏览器发现外部资源的链接时,无论是样式表、脚本还是嵌入的图像引用,它都会发起请求。有些请求是阻塞的,这意味着解析其余 HTML 会暂停,直到导入的资源被处理。浏览器继续解析 HTML,进行请求并构建 DOM,直到到达末尾,此时它会构建 CSS 对象模型。在 DOM 和 CSSOM 完成后,浏览器会构建渲染树,计算所有可见内容的样式。渲染树完成后,就会发生布局,定义渲染树中所有元素的位置和大小。完成后,页面就会在屏幕上渲染或“绘制”。
文档对象模型
DOM 构建是增量的。HTML 响应会变成标记,标记会变成节点,节点会变成 DOM 树。单个 DOM 节点以 startTag 标记开始,以 endTag 标记结束。节点包含有关 HTML 元素的所有相关信息。信息通过标记进行描述。节点根据标记的层级结构连接成 DOM 树。如果在某一对 startTag 和 endTag 标记之间出现了另一对 startTag 和 endTag 标记,那么你就有一个节点包含另一个节点,这就是我们定义 DOM 树层级结构的方式。
节点数量越多,关键渲染路径中后续事件的耗时就越长。进行测量!少几个节点不会造成太大影响,但请记住,添加大量额外节点会影响性能。
CSS 对象模型
DOM 包含页面的所有内容。CSSOM 包含有关如何设置 DOM 样式的全部信息。CSSOM 与 DOM 类似,但有所不同。虽然 DOM 构建是增量的,但 CSSOM 不是。CSS 会阻塞渲染:浏览器会阻止页面渲染,直到收到并处理完所有 CSS。CSS 会阻塞渲染,因为规则可能会被覆盖,所以直到 CSSOM 完成后才能渲染内容。
CSS 有自己的一套规则来识别有效标记。请记住,CSS 中的 C 代表“级联”(Cascade)。CSS 规则向下级联。当解析器将标记转换为节点时,子节点将继承父节点的一些样式。增量处理功能不适用于 CSS,而适用于 HTML,因为后续规则可能会覆盖之前的规则。CSS 对象模型在解析 CSS 时构建,但直到完全解析后才能用于构建渲染树,因为将要被后续解析覆盖的样式不应被渲染到屏幕上。
在选择器性能方面,不那么具体的选择器比更具体的选择器快。例如,.foo {}
比 .bar .foo {}
快,因为在第二种情况下,当浏览器找到 .foo
时,它必须向上遍历 DOM 以检查 .foo
是否有父级 .bar
。更具体的标签需要浏览器做更多的工作,但这种性能损失可能不值得去优化。
如果您测量解析 CSS 所需的时间,您会惊讶于浏览器的速度。更具体的规则成本更高,因为它必须遍历 DOM 树中的更多节点——但这种额外的成本通常很小。首先进行测量。然后根据需要进行优化。特异性可能不是您最容易解决的性能瓶颈。就 CSS 而言,选择器性能优化改进只会带来微秒级的提升。还有优化 CSS 的其他方法,例如缩小化,以及使用媒体查询将延迟加载的 CSS 分离为非阻塞请求。
渲染树
渲染树同时捕获内容和样式:DOM 和 CSSOM 树被合并到渲染树中。为了构建渲染树,浏览器从 DOM 树的根开始检查每个节点,并确定哪些 CSS 规则已附加。
渲染树只捕获可见内容。head 部分(通常)不包含任何可见信息,因此不包含在渲染树中。如果元素上设置了 display: none;
,那么它本身及其所有后代都不会包含在渲染树中。
布局
渲染树构建完成后,布局就成为可能。布局取决于屏幕大小。布局步骤决定了元素在页面上的位置和方式,确定每个元素的宽度和高度,以及它们之间的关系。
元素的宽度是多少?块级元素,顾名思义,其默认宽度为其父元素宽度的 100%。宽度为 50% 的元素将是其父元素宽度的一半。除非另有定义,否则 body 的宽度为 100%,这意味着它将是视口宽度的 100%。设备的宽度会影响布局。
视口元标签定义了布局视口的宽度,从而影响布局。没有它,浏览器将使用默认视口宽度,在默认全屏浏览器中通常为 960px。在默认全屏浏览器(如手机浏览器)中,通过设置 <meta name="viewport" content="width=device-width">
,宽度将是设备的宽度,而不是默认视口宽度。当用户在横屏和竖屏模式之间旋转手机时,设备宽度会发生变化。每次设备旋转或浏览器调整大小时都会发生布局。
布局性能受 DOM 的影响——节点数量越多,布局所需的时间就越长。布局可能成为瓶颈,如果它在滚动或其他动画期间被需要,就会导致卡顿。虽然加载或方向更改时的 20ms 延迟可能没问题,但在动画或滚动时会导致卡顿。任何时候渲染树被修改,例如通过添加节点、更改内容或更新节点的盒模型样式,都会发生布局。
为了减少布局事件的频率和持续时间,请批处理更新并避免对盒模型属性进行动画处理。
绘制
最后一步是将像素绘制到屏幕上。一旦创建了渲染树并发生了布局,就可以将像素绘制到屏幕上。加载时,整个屏幕都会被绘制。之后,只有受影响的屏幕区域会被重绘,因为浏览器经过优化,只需重绘所需的最小区域。绘制时间取决于应用于渲染树的更新类型。虽然绘制是一个非常快速的过程,因此很可能不是提高性能的最有影响力的环节,但重要的是要记住,在衡量动画帧需要多长时间时,要同时考虑布局和重绘时间。应用于每个节点的样式会增加绘制时间,但移除会使绘制时间增加 0.001 毫秒的样式可能不会为您带来最大的优化效益。请记住先进行测量。然后您就可以确定这是否应成为优化重点。
为 CRP 进行优化
通过优先加载哪些资源、控制加载顺序以及减小资源文件大小来提高页面加载速度。性能技巧包括 1) 通过延迟加载非关键资源、将其标记为异步或完全消除它们来最小化关键资源的数量,2) 优化所需的请求数量以及每个请求的文件大小,以及 3) 通过优先下载关键资产来优化关键资源的加载顺序,从而缩短关键路径长度。