浏览器渲染原理
用户看到页面可以分为两个阶段,页面内容加载完毕与页面资源加载完成,分别对应DOMcontentloaded 和load事件
DOMcontentloaded事件触发时,仅到DOM加载完毕
load事件触发时,页面上所有的dom,css,js 等其他资源全部加载完毕
浏览器渲染过程
- 浏览器解析html文档为dom树
- 浏览器解析css文档为cssom树,css对象模型
- 合并dom与cssom为渲染树
- 布局
- 将渲染树各个节点绘制到屏幕上面,这一步为绘制
上述过程并不是顺序执行的,根据实际情况可能会并行、重复执行
DOM树构建过程
- DOM树在构建的过程中可能会被CSS和JS的加载而执行阻塞
display:none
的元素也会在DOM树中- 注释也会在DOM树中
script
标签会在DOM树中
无论是DOM还是CSSOM,都是要经过Bytes→characters→tokens→nodes→object model这个过程。
当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
构建CSS规则树
浏览器解析CSS文件并生成CSSOM,每个CSS文件都被分析成一个StyleSheet对象,每个对象都包含CSS规则。CSS规则对象包含对应于CSS语法的选择器和声明对象以及其他对象。
在这个过程需要注意的是:
- CSS解析可以与DOM解析同时进行。
- CSS解析与
script
的执行互斥 。 - 在Webkit内核中进行了
script
执行优化,只有在JS访问CSS时才会发生互斥。
构建渲染树
通过DOM树和CSS规则树,浏览器就可以通过它两构建渲染树了。浏览器会先从DOM树的根节点开始遍历每个可见节点,然后对每个可见节点找到适配的CSS样式规则并应用。
有以下几点需要注意:
- Render Tree和DOM Tree不完全对应
display: none
的元素不在Render Tree中visibility: hidden
的元素在Render Tree中
渲染树生成后,还是没有办法渲染到屏幕上,渲染到屏幕需要得到各个节点的位置信息,这就需要布局(Layout)的处理了。
渲染树布局
布局阶段会从渲染树的根节点开始遍历,由于渲染树的每个节点都是一个Render Object对象,包含宽高,位置,背景色等样式信息。所以浏览器就可以通过这些样式信息来确定每个节点对象在页面上的确切大小和位置,布局阶段的输出就是我们常说的盒子模型,它会精确地捕获每个元素在屏幕内的确切位置与大小。需要注意的是:
float
元素,absoulte
元素,fixed
元素会发生位置偏移。- 我们常说的脱离文档流,其实就是脱离Render Tree。
渲染树绘制
在绘制阶段,浏览器会遍历渲染树,调用渲染器的paint()方法在屏幕上显示其内容。渲染树的绘制工作是由浏览器的UI后端组件完成的。
浏览器核心部件
主流的渲染引擎有webkit/Gecko
渲染机制的补充
渲染阻塞
js渲染阻塞
JS可以操作DOM来修改DOM结构,可以操作CSSOM来修改节点样式,这就导致了浏览器在遇到<script>
标签时,DOM构建将暂停,直至脚本完成执行,然后继续构建DOM。如果脚本是外部的,会等待脚本下载完毕,再继续解析文档。现在可以在script
标签上增加属性defer
或者async
。脚本解析会将脚本中改变DOM和CSS的地方分别解析出来,追加到DOM树和CSSOM规则树上。
css渲染阻塞
由于CSSOM负责存储渲染信息,浏览器就必须保证在合成渲染树之前,CSSOM是完备的,这种完备是指所有的CSS(内联、内部和外部)都已经下载完,并解析完,只有CSSOM和DOM的解析完全结束,浏览器才会进入下一步的渲染,这就是CSS阻塞渲染。
CSS阻塞渲染意味着,在CSSOM完备前,页面将一直处理白屏状态,这就是为什么样式放在head
中,仅仅是为了更快的解析CSS,保证更快的首次渲染。
回流
当页面的布局发生改变的时候,必然会触发回流,触发回流必然会触发重绘
重绘
当元素的背景颜色,背景色、边框颜色等发生变化的时候,会触发重绘