1. 输入URL网页的请求显示的过程
-
首先通过DNS服务器进行域名解析
-
解析出对应的IP地址,然后从ip地址对应的主机发送http请求 获取对应的静态资源,默认情况服务器会返回index.html文件
- 浏览器内核开始解析HTML
- 首先会解析对应的html 生成DOM Tree
- 解析过程中,如果遇到css的link标签 则会下载对应的css文件
- 下载css文件和生成DOM树是同时进行
- 下载完对应的css文件后会进行css解析 生成CSSOM( CSS object model,CSS对象模型)
- 当DOM Tree和CSS Tree都解析完成之后 会进行合并用来生成Render Tree(渲染树)
-
初步生成的渲染树会显示节点以及部分样式 但是并不表示每个节点的尺寸 位置信息 于是进行Layout(布局)来生成渲染树中节点的宽度 高度位置信息
-
经过Layout之后 浏览器内核将布局时的每个frame转屏幕上的每个像素点 将每个节点绘制到屏幕上
注意: 第一次确定节点的大小位置称之为布局(Layout),之后对节点大小位置改变后的重新计算称之为回流
2. 网页的渲染过程
-
HTML解析 -> DOM Tree
-
CSS解析 -> CSSOM Tree
-
生成 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中
-
-
进行layout布局
- 布局是确定呈现树中所有节点的宽度、高度和位置信息
-
进行paint绘制
- 在绘制阶段,浏览器将布局阶段计算的每个frame转为屏幕上实际的像素点,包括将元素的可见部分进行绘制,比如文本、颜色、边框、阴影、替换元素(比如img)
3. 回流和重绘
3.1 回流
-
理解回流reflow:(也称为重排)
- 第一次确定节点的大小和位置,称之为布局(layout),之后对节点的大小、位置修改重新计算称之为回流。
-
什么情况下会引起引起回流?
-
DOM结构发生改变(添加新的节点或者移除节点)
-
改变了布局(修改了width、height、padding、font-size等值)
-
窗口resize(修改了窗口的尺寸等)
-
调用getComputedStyle方法获取尺寸、位置信息
-
3.2 重绘
-
理解重绘repaint:
- 第一次渲染内容称之为绘制(paint),之后重新渲染称之为重绘。
-
什么情况下会引起重绘?
- 修改背景色、文字颜色、边框颜色、样式等
3.3 总结
- 回流一定会引起重绘,所以回流很消耗性能,应该尽量避免回流
- 如何避免回流?
-
修改样式时尽量一次性修改
- 比如通过cssText修改,比如通过添加class修改
-
尽量避免频繁的操作DOM
- 可以在一个DocumentFragment或者父元素中,将要操作的DOM操作完成,再一次性的操作
-
尽量避免通过getComputedStyle获取尺寸、位置等信息
-
对某些元素使用position的absolute或者fixed
- 并不是不会引起回流,而是开销相对较小,不会对其他元素造成影响
-
4. 合成composite
绘制的过程,可以将布局后的元素绘制到多个合成图层中,是浏览器的一种优化手段
- 默认情况下,标准流中的内容都是被绘制在同一个图层中
- 某些特殊的CSS属性, 会生成新的合成图层(每个合成层都是单独渲染的):
- 3D transforms
- video、canvas、iframe
- opacity 动画转换时
- position: fixed
- will-change:一个实验性的属性,提前告诉浏览器元素可能发生哪些变化
- animation 或 transition 设置了opacity、transform
- 有动画时尽量使用transform或者opacity,性能更高
- 注意:分层确实可以提高性能,但是它以内存管理为代价,因此不应作为 web 性能优化策略的一部分过度使用。
5. defer和async
5.1 script元素和页面解析的关系
-
页面渲染时的JavaScript
- 浏览器在解析HTML的过程中,遇到了script元素是不能继续构建DOM树的,它会停止继续构建,首先下载JavaScript代码,并且执行JavaScript的脚本,只有等到JavaScript脚本执行结束后,才会继续解析HTML,构建DOM树
- 这是因为JavaScript的作用之一就是操作DOM,并且可以修改DOM,如果等到DOM树构建完成并且渲染再执行JavaScript,会造成严重的回流和重绘,影响页面的性能。所以会在遇到script元素时,优先下载和执行JavaScript代码,再继续构建DOM树。
-
这个带来新的问题,特别是现代页面开发中:
- 在目前的开发模式中(比如Vue、React),脚本往往比HTML页面更“重”,处理时间需要更长
- 会造成页面的解析阻塞,在脚本下载、执行完成之前,用户在界面上什么都看不到
-
为了解决这个问题,script元素给我们提供了两个属性(attribute):
defer和async
5.2 defer
-
defer属性告诉浏览器不要等待脚本下载,而继续解析HTML,构建DOM Tree。-
脚本会由浏览器来进行下载,但是不会阻塞DOM Tree的构建过程
-
如果脚本提前下载好了,它会等待DOM Tree构建完成,在DOMContentLoaded事件之前先执行defer中的代码
-
-
多个带defer的脚本是可以保持正确的顺序执行的。
-
从某种角度来说,defer可以提高页面的性能,并且推荐放到head元素中
-
注意:defer仅适用于外部脚本,对于script默认内容会被忽略
5.3 async
-
async是让一个脚本完全独立的:
-
浏览器不会因 async 脚本而阻塞(与 defer 类似)
-
async脚本不能保证顺序,它是独立下载、独立运行,不会等待其他脚本
-
async不能保证在DOMContentLoaded之前或者之后执行
-
5.4 defer 和 async的区别
-
defer通常用于需要在文档解析后操作DOM的JavaScript代码,并且对多个script文件有顺序要求的
-
async通常用于独立的脚本,对其他脚本,甚至DOM没有依赖的