默认情况下服务器会给浏览器返回index.html文件,所以解析html是所有步骤的开始
How Browsers Work: Behind the scenes of modern web browsers - HTML5 Rocks
1.解析html
构建DOM Tree
2.生成css规则
在解析的过程中,如果遇到css的link元素,那么会由浏览器负责下载对应的css文件。
下载css文件不会影响DOM的解析
浏览器下载完css文件后,就会对css文件进行解析,解析出对应的规则树,称之为CSSOM
3.构建Render Tree
有DOM Tree和CSSOM Tree后,就可以两个结合来构建Render Tree了
- link元素不会阻塞DOM Tree的构建,但是会阻塞Render Tree的构建,因为Render Tree在构建的时候,需要对应的CSSOM Tree
- Render Tree和DOM Tree并不是一一对应的关系,比如display:none的元素,不会出现在Render Tree上
4.布局(layout)和绘制(paint)
第四步是在Render Tree渲染树上运行Layout布局,计算每个节点的几何体
- 渲染树显示有哪些节点以及其他样式,但是不表示每个节点的尺寸和位置信息
- 布局Layout是确定呈现树中所有节点的宽度、高度和位置信息
第五步是将每个节点绘制paint在屏幕上
- 在绘制阶段,浏览器将布局阶段计算的每个frame转为屏幕上实际的像素点
- 将元素的可见部分进行绘制,比如文本、颜色、边框、阴影、替换元素
5.回流和重绘
回流reflow,也可以称之为重排
- 第一次确定节点的大小和位置,称之为布局
- 之后对节点的大小、位置修改重新计算称之为回流
什么情况会引起回流?
- 比如DOM结构发生改变(添加新的节点或者移除节点)
- 改变了布局(修改了width、height、padding、font-size)
- 修改了窗口的尺寸
- 调用getComputedStyle方法获取尺寸、位置信息
重绘repaint
- 第一次渲染内容称之为绘制
- 之后重新渲染称之为重绘
什么情况会引起重绘?
- 比如修改背景色、文字颜色、边框颜色、样式等
回流一定会引起重绘,回流是很消耗性能的
所以在开发的时候要避免回流
- 修改样式时尽量一次性修改,如果通过cssText修改,通过添加class修改
- 尽量避免频繁操作DOM
- 尽量避免通过getComputedStyle获取尺寸、位置等信息
- 对某些元素使用position的absolute或者fixed,会回流但是开销相对较小,不会对其他元素造成影响
6.特殊解析-composite合成
绘制过程中,可以将布局后的元素绘制到多个合成图层中,是一种浏览器的优化手段。
默认情况下,标准流中的内容时被绘制在同一个图层中的;
一些特殊的属性,会创建一个新的合成层,新的图层可以利用GPU来加速绘制,因为每个图层都是单独渲染的。
常见的属性可以形成新的合成层
- 3D transforms
- video、canvas、iframe
- opacity动画转换时
- position: fixed
- will-change
- animation或transition设置了opacity、transform
7.script元素和页面解析的关系
事实上,浏览器在解析html的过程中,遇到script元素时不能继续构建DOM树的;
它会停止继续构建,首先下载JavaScript代码,并且执行JavaScript的脚本;
只有等JavaScript脚本执行结束后,才会继续解析html,构建DOM树。
为什么不继续构建DOM树呢?
- 因为JavaScript的作用之一就是操作DOM,并且可以修改DOM;
- 如果我们等到DOM树构建完成并且渲染再执行JavaScript,会造成严重的回流和重绘
如果不继续构建DOM树往往会带来新的问题,因为再目前的开发模式中(比如Vue、React),脚本往往比html页面更“重” ,处理的时间更长。所以会造成页面的解析阻塞,在脚本下载、执行完成之前,用户在界面上什么都看不到。
为了解决这个问题,script元素给我们提供了两个属性attribute:defer、async
8.defer属性
defer属性告诉浏览器不要等待脚本下载,而继续解析html、构建DOM Tree
- 脚本会由浏览器来进行下载,但是不会阻塞DOM Tree的构建过程
- 如果脚本提前下载好了,它会等待DOM Tree构建完成,在DOMContentLoaded事件之前执行defer中的代码
DOMContentLoaded总是会等待defer中的代码先执行完成
多个带defer的脚本是可以保持正确的顺序执行的,
defer可以提高页面的性能,并且推荐放到head元素中。
9.async属性
async和defer有些类似,它也能够让脚本不阻塞页面。
async能让一个脚本完全独立,不能保证顺序,它是独立下载、独立运行的,也不会等待其他脚本,所以async通常用于独立的脚本,对于DOM没有依赖的情况