定义
优先显示与用户操作有关的内容。浏览器接收到 HTML、CSS、JavaScript 字节,到转化成浏览器内的像素点。这一系列的有一个中间过程。性能优化就是了解这一中间过程,即关键渲染路径。
原理
浏览器如何渲染的👇
- 字节 → 字符 → 令牌 → 节点 → 对象模型。
- HTML 标记转换成文档对象模型 (DOM);CSS 标记转换成 CSS 对象模型 (CSSOM)。
- DOM 和 CSSOM 是独立的数据结构。
- DOM 树与 CSSOM 树合并后形成渲染树。
- 渲染树只包含渲染网页所需的节点。
- 布局计算每个对象的精确位置和大小。
- 最后一步是绘制,使用最终渲染树将像素渲染到屏幕上。
当浏览器请求某个网址时,会将二进制字节转化成指定编码的字符:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
<title>Critical Path</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
</body>
</html>
一、处理 DOM
- 转换:原始字节到字符编码
- 令牌化:将字符转化成令牌,每个令牌都具有特殊含义和一组规则。例如:
<html>
<head>
- 词法分析:将令牌转化成满足某种属性和规则的节点对象
- 构建 DOM 树:构建父子关系,以及树形数据结构
二、处理 CSSOM
和处理 DOM 的过程类似。
body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }
CSS 字节转换成字符,接着转换成令牌和节点,最后链接到一个称为 “CSS 对象模型”(CSSOM) 的树结构内:
三、处理渲染树
合并 DOM、CSSOM 成为渲染树
四、布局
计算每个对象的位置和大小,如果设置了 display: none;
则不参与布局。
五、绘制
将最终渲染树打印在浏览器上,就完成了我们平常看到的栩栩如生的页面。
如何优化
关键点
- CSS 是阻塞渲染的资源。需要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间
- 让 JavaScript 异步执行,并去除关键渲染路径中任何不必要的 JavaScript
- 使用 Lighthouse 审查页面,按照提示做相应的调整
- 最大限度减小以下三种可变因素
- 关键资源的数量:删除它们,延迟它们的下载,将它们标记为异步等
- 关键路径长度:优化关键字节数(比如网页大小)以缩短下载时间
- 关键字节的数量:优化其余关键资源的加载顺序,尽早下载所有关键资产
- 消除阻塞渲染的 CSS 和 JavaScript
- 优化 JavaScript 的使用:默认情况下,JavaScript 资源会阻塞解析器
- 首选异步加载
- 避免同步服务器调用,比如使用异步方法
navigator.sendBeacon()
用于埋点数据上报 - 延迟解析 JavaScript
- 避免运行时间长的 JavaScript
- 优化 CSS 的使用:首次构建网页时,JavaScript 常常受阻于 CSS,确保非关键资源标记为异步
- 将 CSS 置于 head 标签内
- 避免使用 CSS import
- 将关键 CSS 直接内联到 HTML 文档内
JavaScript 执行时序👇
案例
优化前
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
<script src="app.js"></script>
</body>
</html>
解释:
- 关键渲染路径有3个:HTML、CSS、JavaScript 图中蓝色部分
- 注意
Render process
和blocking
部分- JS 运行发生于 CSSOM 完成后
- CSS 和 JS 都是阻塞分析器的
优化后
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet" media="print">
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
<script src="app.js" async></script>
</body>
</html>
解释:
- 脚本不再阻止解析器,也不再是关键渲染路径的组成部分
- 由于没有其他关键脚本,CSS 也不需要阻止 domContentLoaded 事件
- domContentLoaded 事件触发得越早,其他应用逻辑开始执行的时间就越早