浏览器渲染机制
不同的浏览器内核不同,渲染过程中有的细节也不一样,以webkit
主流程为例:
浏览器解析渲染页面大致过程:
1、DOM Tree
:浏览器向服务器发送http
请求,服务器响应http
请求并发送文档给浏览器,浏览器解析HTML
构建DOM
树。
其中HTML Parser就起到了将HTML标记解析成DOM Tree的作用,HTML Parser将文本的HTML文档,提炼出关键信息,嵌套层级的树形结构,便于计算拓展;这其中也有很多的规则和操作,比如容错机制,识别特殊标签
<br></br>
等
2、CSSOM
:浏览器解析CSS
构建CSSOM
树。
CSS Parser将很多个CSS文件中的样式合并解析出具有树形结构Style Rules,也叫做CSSOM
其中还有一个细节是浏览器解析文档:当遇到
<script>
标签的时候会停止解析文档,立即解析脚本,将脚本中改变DOM和CSS的地方分别解析出来,追加到DOM Tree和CSSOM上
3、Render Tree
:把DOM
树和CSSOM
树组合构建渲染树Rander Tree
,有了渲染树,浏览器就知道网页上有哪些节点以及每个节点的CSS
属性。
Render Tree的构建其实就是DOM Tree和CSSOM Attach的过程,在webkit中,解析样式和创建呈现器的过程称为”附加”,每个DOM节点都有一个”attach”方法,Render Tree其实就相当于一个计算好样式,与HTML对应的Tree
4、Layout
:根据Render
树进行布局渲染render layer
,计算每个节点的几何结构(也就是计算出每个节点在屏幕上的位置)。
创建渲染树后,Layout根据根据渲染树中渲染对象的信息,计算好每一个渲染对象的位置和尺寸,将其放在浏览器窗口的正确位置,某些时候会在文档布局完成之后进行DOM修改,重新布局的过程就称为回流
5、Painting
:将计算好的每个节点的布局信息绘制到屏幕上。
绘制阶段则会遍历呈现树,并调用呈现器的paint方法,将呈现器的内容显示在屏幕上,绘制的顺序其实就是元素进入堆栈样式上下文的顺序,例如,块呈现器的堆栈顺序如下:1.背景颜色,2.背景图片,3.边框,4.子代,5.轮廓
重绘(Repaint
)和回流/重排(Reflow
)
概念
重绘:样式发生改变,位置没有发生变动,页面不需要重新计算一次,重绘一次即可。
回流:位置发生了变化影响了其他元素的位置,需要重新计算位置即回流/重排,之后重绘。
重绘不一定回流,但回流必定重绘
重绘和回流的区别
- 回流指当前窗口发生改变,发生滚动操作,或者元素的位置大小相关属性被更新时会触发布局过程,发生在render树,比如元素的几何尺寸变化,就需要重新验证并计算Render Tree
- 重绘指当前视觉样式属性被更新时触发的绘制过程,发生在渲染层render layer
- 回流的成本比重绘高
重绘和回流的应用
1、回流
- 增加、删除、更新
DOM
节点 - 通过
display: none
隐藏一个DOM
节点(位置发生改变) - 元素尺寸发生变化(如边距)
- 让一个
DOM
节点动画时 - 添加样式,让整个样式发生改变
- 改变窗口尺寸和滚动窗口
计算offsetWidth、scrollTop、clientTop、getComputedStyle()等属性
(获取这些属性的信息时需要返回新的布局信息,会强制队列刷新,触发回流)
2、重绘
- 通过
visibility: hidden
隐藏一个节点需要重绘
减少回流重绘次数的方法
- 避免一条一条的修改DOM样式,而是修改className或者style.classText
- 对元素进行一个复杂的操作,可以先隐藏它,操作完成后在显示
- 在需要经常获取那些引起浏览器回流的属性值时,要缓存到变量中
- 不使用table布局,一个小的改动可能就会引起整个table重新布局
- 在内存中多次操作节点,完成后在添加到文档中
白屏
页面没有加载完,就会出现白屏。
- 对IE来说,把样式放在底部时,在某些场景下(如打开新窗口/刷新页面等)页面会出现白屏,而不是内容逐步展现。
- 如果使用@import标签,即使将CSS写入外部样式表由link引入并放在头部,也可能出现白屏。
- 把js文件放入页面顶部而未使用defer或async延迟或异步加载js文件,从而阻塞html与css的加载也会导致白屏。
FOUC
(Flash of unsettled content)无样式内容闪烁。 页面出现FOUC现象,具体表现为逐步加载无样式的内容,等CSS加载完成后页面突然展现样式。
把样式放在底部时,会先显示已加载的html内容,再逐步加载无样式内容,等css全部加载完成后页面突然展现样式。
CSS 和 JS 最佳放置顺序
- 使用 link 标签将样式表放在顶部
- 将JS放在底部
JS的阻塞问题
- JS会阻塞DOM树的解析和渲染
- 若JS位于页面顶部
- JS脚本会阻塞后面内容的呈现
- JS脚本会阻塞其后组件(如图片)的下载
- JS加载时间过长,css需等待,则会出现一段时间白屏
CSS的阻塞问题
- CSS会阻塞DOM树的渲染 (渲染树是依赖于CSSOM和DOM的,必须要等到CSSOM构建完成才能开始渲染)
- CSS可能会阻塞DOM树的解析(若CSS阻塞JS语句,JS会阻塞DOM,则CSS可能阻塞DOM)
- CSS会阻塞其后面JS语句的执行(JS可能会用到DOM节点和CSS样式)
- 是一种优化机制避免回流
异步加载
<script src="script.js"></script>
放在顶部的这个js文件,会提前加载,如何使它在顶部仍然稍后加载?
async (异步脚本)
不让页面等待脚本下载和执行,从而异步加载页面其他内容(并行),异步脚本会在页面的load事件前执行。不保证顺序。
<script async src="script.js"></script>
defer(延迟脚本)
js脚本会被延迟到整个页面都解析完成后再运行,会先于DOMContentLoaded事件执行。按顺序执行。
<script defer src="script.js"></script>
作用:缩短了网页的加载时间,且他们的显示速度更快
页面加载
对于浏览器来说,页面加载主要有两个事件。DOMContentLoaded、onLoad
。
onLoad
等待页面所有
资源加载完成
才会触发。
DOMContentLoaded
等页面
内容解析
完就触发。
- 若js在css前,则DOMContentLoaded不会等待css加载,也不会等待之后的图片、视频等其他资源加载。
- 若js在css后,CSS阻塞其后面的js语句执行,js阻塞DOM解析,就导致DOMContentLoaded会等待CSS加载完执行。
参考引用:
https://juejin.im/post/6844903878949863432
https://github.com/okaychen/FE-Interview-Brochure/blob/master/html-and-css.md