优化关键渲染路径,加速浏览器首次渲染
amandakelake opened this issue · comments
优化关键渲染路径能够让浏览器尽可能快地绘制网页:更快的网页渲染速度可以提高吸引力、增加网页浏览量以及提高转化率。为了最大程度减少访客看到空白屏幕的时间,我们需要优化加载的资源及其加载顺序。
分析关键渲染路径性能 | Web | Google Developers
渲染树构建、布局及绘制
- 处理 HTML 标记并构建 DOM 树,初始的 HTML 被完全加载和解析后会触发
DOMContentLoaded
事件 - 处理 CSS 标记并构建 CSSOM 树。
- 将 DOM 与 CSSOM 合并成一个渲染树renderTree。
- 根据渲染树来布局,以计算每个节点的几何信息。
- 将各个节点绘制到屏幕上。
优化关键渲染路径_就是指最大限度缩短执行上述第 1 步至第 5 步耗费的总时间。 这样一来,就能尽快将内容渲染到屏幕上,此外还能缩短首次渲染后屏幕刷新的时间,即为交互式内容实现更高的刷新率。
阻塞渲染的CSS
在渲染树构建中,HTML 和 CSS 都是阻塞渲染的资源,HTML 显然是必需的,因为如果没有 DOM,我们就没有可渲染的内容,但 CSS 的必要性可能就不太明显
- 默认情况下,CSS被视为阻塞渲染的资源
- 我们可以通过媒体类型和媒体查询将一些CSS资源标记为不阻塞渲染
- 浏览器会下载所有CSS资源,无论阻塞还是不阻塞,所以阻塞渲染仅仅是指浏览器是否需要暂停网页的首次渲染,直至该资源准备就绪
<style>/* styles here */</style>
{{-- link标签需要加一个网络来回的时间开销 --}}
<link href="style.css" rel="stylesheet">
<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 40em)">
几种引入CSS资源的方法,是否阻塞首次渲染
- link引入,是
- style标签直接写入,是
- 通过DOM api引入, 否
const style = document.createElement('link');
style.rel = 'stylesheet';
style.href = 'index.css';
document.head.appendChild(style);
- 使用preload方式载入CSS,否
<link rel="preload" href="index_print.css" as="style" onload="this.rel='stylesheet'">
- 为link添加media query, 当媒体查询不匹配时,不会阻塞
注意
- CSS选择器越详细,匹配工作越多,匹配节点越慢
- renderTree只包含了渲染网页所需要的元素,不需要的是不会被添加进去的,比如
<head>
,display: none的节点
设置了visibility:hidden
的元素虽不可见,但仍然占有空间,仍会被添加到RenderTree - layout是指计算DOM元素在最终屏幕上显示的大小和位置,由于web页面的元素布局是相对的,所以任意其中一个元素的位置发生变化,都会联动其他元素,就会产生reflow(回流)
- 屏幕旋转
- 浏览器视窗改变,触发resize事件
- 与大小和位置相关的CSS属性发生改变
- 尽量不用table布局,可能很小的一个小改动会造成整个 table 的重新布局
repaint(重绘):节点需要更改外观,但不改变布局和几何属性,就叫做重绘,比如color
JS对渲染的影响
JavaScript 可以查询和修改 DOM 与 CSSOM
由于浏览器不了解脚本计划在页面上执行什么操作,它会作最坏的假设并阻止解析器。当 HTML 解析器遇到一个 script 标记时,它会暂停构建 DOM,将控制权移交给 JavaScript 引擎;等 JavaScript 引擎运行完毕,浏览器会从中断的地方恢复 DOM 构建。因此,执行内联脚本会阻止 DOM 构建,也就延缓了首次渲染
另外,由于JS可以读取和修改CSSOM属性
如果浏览器尚未完成 CSSOM 的下载和构建,这时遇到了脚本需要运行,浏览器将延迟脚本执行和 DOM 构建,直至其完成 CSSOM 的下载和构建
如果是外部 JavaScript 文件,浏览器必须停下来,等待从磁盘、缓存或远程服务器获取脚本,这就可能给关键渲染路径增加数十至数千毫秒的延迟
所以可以通过标记外部JS文件为异步(async/defer),指示浏览器在等待脚本可用期间不阻止DOM构建
另外,对于执行时间过长的JS代码,可以考虑用Web Worker
去处理,它不会操作DOM元素,只能做一些纯计算的工作
JavaScript代码运行在浏览器的主线程上,但浏览器的主线程还负责样式计算、布局、绘制的工作,所以JS运行时间过长会阻塞其他渲染工作,导致丢帧,每帧的渲染应该在16ms内完成,而动画占据了不少时间,所以JS代码的运行消耗应该控制在3~4ms
其他资源的影响
构建渲染树甚至绘制网页时无需等待页面上的每个资产:并非所有资源都对快速提供首次绘制具有关键作用。
谈论关键渲染路径时,通常谈论的是 HTML 标记、CSS 和 JavaScript。图像不会阻止页面的首次渲染
优化关键渲染路径
为尽快完成首次渲染,我们需要最大限度减小以下三种可变因素:
- 关键资源的数量:可能阻止网页首次渲染的资源
- 关键路径长度:获取所有关键资源所需的往返次数或总时间
- 关键字节的数量:所有关键资源传送文件大小的总和
优化关键渲染路径的常规步骤如下
- 对关键路径进行分析和特性描述:资源数、字节数、长度
- 最大限度减少关键资源的数量:删除它们,延迟它们的下载,将它们标记为异步等
- 优化关键字节数以缩短下载时间(往返次数)
- 优化其余关键资源的加载顺序:您需要尽早下载所有关键资产,以缩短关键路径长度
PageSpeed 规则和建议
- 消除阻塞渲染的 JavaScript 和 CSS
- 优化 JavaScript 的使用
- 首选使用异步 JavaScript 资源 async
- 延迟解析 JavaScript defer
- 避免运行时间长的 JavaScript,也就是常说的拆分代码
- 优化 CSS 的使用
- 将 CSS 置于文档 head 标签内,浏览器尽早发现 标记并尽早发出 CSS 请求
- 避免使用 CSS import,它们会在关键路径中增加往返次数
- 内联阻塞渲染的 CSS,不会增加关键路径中的往返次数