关键渲染路径
关键渲染路径是浏览器将 HTML、CSS 和 JavaScript 转换为屏幕上的像素所经历的步骤序列。优化关键渲染路径可提高渲染性能。关键渲染路径包含了文档对象模型(DOM)、CSS 对象模型(CSSOM)、渲染树和布局。
在解析 HTML 时会创建 DOM。HTML 可以请求 JavaScript,进而可能反过来修改 DOM。HTML 也可以包含或请求样式,进而构建 CSSOM。浏览器引擎将两者结合起来以创建渲染树。布局确定页面上所有内容的大小和位置。当布局明确后,浏览器就会将像素绘制到屏幕上。
优化关键渲染路径可以缩短首次渲染的时间。了解和优化关键渲染路径对于确保重排和重绘能以每秒 60 帧的速度进行、确保高效的用户交互、避免卡顿而言是至关重要的。
理解 CRP
Web 性能包含了服务器请求和响应、加载、执行脚本、渲染、布局和绘制每个像素到屏幕上。
网页请求从 HTML 文件请求开始。服务器返回包含 HTML 的响应。浏览器开始解析 HTML,将收到的数据转换为 DOM 树。当浏览器每次发现 DOM 树包含外部资源就会初始化其请求(无论是样式、脚本或者嵌入的图片引用)。有时这些请求会阻塞,意味着解析剩下的 HTML 会被终止,直到前面这些重要的资源得到处理。浏览器接着解析 HTML,发送请求并构造 DOM,直到 HTML 的文件结尾,此时就会开始构造 CSSOM。等到 DOM 和 CSSOM 完成之后,浏览器构造渲染树,计算所有可见内容的样式。一旦渲染树完成,布局就会开始,定义所有渲染树元素的位置和大小。在布局完成后,页面被渲染完成(被绘制到屏幕上)。
文档对象模型
DOM 构建是增量的。HTML 响应变成标记(token),标记变成节点,而节点又变成 DOM 树。单个 DOM 节点以起始标签的令牌开始,以结束标签的令牌结束。节点包含有关 HTML 元素的(使用标记描述的)所有相关信息。节点根据标记的层次结构连接到 DOM 树中。如果另一组起始标签和结束标签标记位于一组起始标签和结束标签之间,则你在节点内有一个节点。这也是我们定义 DOM 树层次结构的方式。
节点数量越多,关键渲染路径中的后续事件将花费的时间就越长。测一下吧!几个额外的节点不会有什么区别,但要一直记住,增添太多的节点会影响性能。
CSS 对象模型
DOM 包含页面所有的内容。CSSOM 包含了页面所有的样式,也就是如何展示 DOM 的信息。CSSOM 跟 DOM 很像,但是并不相同:DOM 构造是增量的,CSSOM 却不是;CSS 是渲染阻塞的:浏览器会阻塞页面渲染直到它接收和执行了所有的 CSS。CSS 是渲染阻塞是因为规则可以被覆盖,所以直至 CSSOM 构建完成之前,内容都不能被渲染。
CSS 有其自身的规则集合用来定义标识。注意 CSS 中的 C 代表的是“层叠”。CSS 规则是级联的。随着解析器转换标识为节点,节点的后代继承了样式。像处理 HTML 那样的增量处理功能没有被应用到 CSS 上,因为后续规则可能被之前的所覆盖。CSS 对象模型随着 CSS 的解析而被构建,但是直到完成都不能被用来构建渲染树,因为样式将会被之后的解析所覆盖而不应该被渲染到屏幕上。
从选择器性能的角度,更少的特定选择器是比更多的要快。例如,.foo {}
是比 .bar .foo {}
更快的因为当浏览器发现 .foo
,接下来必须沿着 DOM 向上走来检查 .foo
是不是有一个祖先 .bar
。越是具体的标签浏览器就需要更多的工作,但这样的弊端未必值得优化。
如果你测量过解析 CSS 的时间,你将会被浏览器实际的“快”所震惊。更具体的规则更耗时,因为这些规则必须遍历更多的 DOM 树节点,但这所带来的额外的消耗通常很小。你可以先测量一下。然后按需优化。特定化或许不是最适合你的。在 CSS 中选择器的性能优化,提升仅仅是毫秒级的。还有其他一些方式来优化 CSS,例如极简化和使用媒体查询,从而异步处理 CSS 为非阻塞的请求。
渲染树
渲染树包括了内容和样式:DOM 和 CSSOM 树结合为渲染树。为了构造渲染树,浏览器检查每个节点,从 DOM 树的根节点开始,并且决定哪些 CSS 规则被添加。
渲染树只包含了可见内容。头部(通常)不包含任何可见信息,因此不会被包含在渲染树中。如果有元素上有 display: none;
,它本身和其后代都不会出现在渲染树中。
布局
一旦渲染树被构建,布局变成了可能。布局取决于屏幕的尺寸。布局这个步骤决定了在哪里和如何在页面上放置元素,决定了每个元素的宽和高,以及他们之间的相关性。
什么是元素的宽?块级元素,根据定义,默认有父级宽度的 100%。一个宽度为 50% 的元素,将占据父级宽度的一半。除非另外定义,body 有 100% 的宽,意味着它占据视口宽度的 100%。设备的宽度影响布局。
视口的元标签定义了布局视口的宽度,从而影响布局。没有的话,浏览器使用视口的默认宽度,默认全屏浏览器通常是 960px。在默认情况下像你的手机浏览器的全屏浏览器,通过设置 <meta name="viewport" content="width=device-width">
,宽度将会是设备的宽度而不是默认的视口宽度。当用户在横向和纵向模式旋转他们的设备时,设备宽度将会改变。布局发生在每次设备旋转或浏览器缩放时。
布局性能受 DOM 影响——节点数越多,布局就需要更长的时间。布局将会变成瓶颈,如果期间需要滚动或者其他动画将会导致迟滞。20ms 的延迟在加载或者方向改变时或许还可以接受,但在动画或滚动时就会变得卡顿。任何渲染树改变的时候,像添加节点、改变内容或者在一个节点更新盒模型样式的时候就会开始布局。
为了减小布局事件的频率和时长,我们应该批量更新或者避免改动盒模型属性。
绘制
最后一步是将像素绘制在屏幕上。一旦渲染树创建并且布局完成,像素就可以被绘制在屏幕上。加载时,整个屏幕被绘制出来。之后,只有受影响的屏幕区域会被重绘,浏览器被优化为只重绘需要绘制的最小区域。绘制时间取决于何种类型的更新被附加在渲染树上。绘制是一个非常快的过程,所以聚焦在提升性能时这大概不是最有效的部分,重点要记住的是当测量一个动画帧需要的时间需要考虑到布局和重绘时间。添加到节点的样式会增加渲染时间,但是移除样式增加的 0.001ms 或许不能让你的优化物有所值。记住先测量。然后你可决定它的优化优先级。
优化 CRP
提升页面加载速度需要通过被加载资源的优先级、控制它们加载的顺序和减小这些资源的体积。性能提示包含 1)通过异步、延迟加载或者消除非关键资源来减少关键资源的请求数量,2)优化必须的请求数量和每个请求的文件体积,3)通过区分关键资源的优先级来优化被加载关键资源的顺序,来缩短关键路径长度。