一、CRP引入
1、什么是CRP?
关键渲染路径是浏览器将 HTML、CSS JavaScript 转换为在屏幕上呈现的像素内容所经历的一系列步骤。
2、为什么要了解CRP?
在浏览器输入网址向服务器发送请求后返回html等相关内容,那么浏览器是怎样将内容呈现到网页上的呢?当了解了这一过程之后才能进一步提升网站的渲染效率,进而优化,那么接下来先了解CRP的执行过程把。
二、CRP执行步骤
1、总览
①构建DOM
- 将Html解析成许多Tokens
- 将Tokens转换成nodes节点
- 将nodes节点组合汇聚成Dom树
②构建CSSOM(CSS Object Model)
- 解析css文件,并构建CSSOM树(过程类似DOM)
③构建Render
- 将生成的DOM树和CSSOM树结合生成Render树
④Layout
- 对比每个元素的位置并计算出相对于布局视口的尺寸
⑤Paint
- 根据布局将render树转换成像素最终绘制到屏幕上
其中DOM树的构建是逐步进行的,而CSSOM是完整构建的,至于JS是会阻塞DOM的构建,后面会详细介绍。
2、DOM树的构建
首先浏览器从服务器获取到的html文档流本身是二进制文件,通过浏览器解析生成对应的Tokens令牌(一个个以StartTag标识开始标签,EndTag标识结束标签,以及其他具体内容),在将所有的Tokens令牌转换成Nodes节点,最终组合成对应的DOM树。
3、构建CSSOM
假设有如下css样式:
body {
font-size: 16px;
}
p {
font-weight: bold;
}
span {
color: red;
}
p span {
display: none;
}
img {
float: right;
}
那么最终构建的cssom树如下(其中和DOM构建方式类似)
4、构建Render Tree
如果只从概念上看很容易误导Render Tree就是简单的DOM+CSSOM的结合,其实还是有所区别的,因为浏览器只构建在屏幕上显示的内容,如html中<head>和<meta>就无须构建了,以及使用display:none不显示的内容都不会构建,即只遍历每个可见的节点。
(注:隐藏的组件仍然还会构建,如:visibility: hidden )
可以发现p下的span是不可见的,因此最终Render树并没有构建。
5、Layout
目前我们已经得到节点的所有信息了,但是还不知道相对于当前Viewport的位置和大小,Layout就是计算这两个信息的。
<meta name="viewport" content="width=device-width">这一属性即定义了当前视口宽度大小等于设备宽度。
这块就不在赘述。
6、Paint
浏览器将每一个节点以像素显示在屏幕上,最终我们看到页面。
7、js对DOM和CSSOM的影响
<html>
<head>
<meta name="viewport" content="width=device-width">
<link href="style.css" ref="stylesheet">
</head>
<body>
<p>
Awesome page
<script>
var e=document.getElementsByTagName('p')[0];
e.style.color="red";
</script>
is awesome
</p>
</body>
</html>
p{ color:black}
这里我们需要知道一件事情就是浏览器是多线程的,js是单线程的,因此,其实本身DOM树的构建和CSSOM树的构建是异步的互不影响。知道这一点之后我们再来思考一个问题,script标签里面包裹的js内容可能会改变DOM树的内容也可能改变CSSOM树的内容,但是编译器无法知晓所以js具体影响的内容所以索性会阻塞DOM树的构建,等到js运行完成在继续进行DOM树的构建,但是问题又来了,CSSOM树的也可能会受到影响呀,所以就规定了等到CSSOM构建完毕在运行js,也就是说js的执行需要等到css加载完毕才会执行,因此最终呈现的效果图为:
再来看如下代码采用异步加载js的方式:
<html>
<head>
<meta name="viewport" content="width=device-width">
<link href="style.css" ref="stylesheet">
<script src="main.js" async></script>//这里主要讲解使用的async
</head>
<body>
<p> Awesome page is awesome</p>
</body>
</html>
如果使用异步脚本,脚本的网络请求优先级降低,且网络请求期间不阻塞 DOM 构建,直到请求完成才开始执行脚本。(注:内联js使用async无效)
参考文章:
【1】深入理解浏览器解析渲染 HTML 深入理解浏览器解析渲染 HTML - 知乎
【3】web Performance Optimization https://classroom.udacity.comhttps://classroom.udacity.com/me