当浏览器的网络线程收到HTML文档后,会产生一个渲染任务,并将其传递给渲染主线程消息队列,在事件循环的机制的作用下,渲染主线程取出消息队列中的渲染任务,开启渲染流程。
渲染的第一步解析HTML 解析过程中遇到CSS解析CSS,遇到JS执行JS,为了提高解析效率,浏览器在开始解析前,会启动一个预解析的线程,率先下载HTML中的外部CSS文件和JS文件
如果主线程解析到Link位置,此时外部的CSS文件还没下载解析好,主线程不会等待,继续解析后续的HTML,这是因为下载和解析CSS的工作是在预解析线程中进行的,这就是CSS不会阻塞HTML解析的根本原因
如果主线程解析到script位置,会停止解析HTML,转而等待JS文件下载好(Js代码只会读取一遍),并将全局代码解析执行完成后,才能继续解析HTML,这就是因为JS代码的执行可能会修改当前的DOM树,所以DOM树的生成必须暂停,这就是JS会阻塞HTML解析的根本原因
第一步完成后,会得到DOM树和CSSOM树,浏览器的默认样式,外部样式,行内样式均会包含在CSSOM树中
渲染的下一步是样式计算 主线程会遍历得到的DOM树,依次为树中的每个节点计算出它最终的样式,称之为Computed Style在这一过程中,很多预设值会变成绝对值,比如red会变成rgb(255,0,0),相对单位变成绝对单位,比如em变成px,这一步完成后,会得到一颗带有样式的DOM树
接下来是布局。 布局完成后会得到布局树。 布局阶段会依次遍历DOM树的每一个节点,计算每个节点的几何信息。例如节点的宽高,相对包含的位置。大部分时候,DOM树和布局树并非一一对应。比如display:none的节点没有任何几何信息。因此不会生成布局树;又比如使用了伪元素选择器,虽然DOM树中不存在这些伪元素节点,但它们拥有几何信息,所以会生成到布局树中。还有匿名行盒、匿名块盒等等都会导致DOM树和布局树无法一一对用
下一步是分层 主线程会使用一套复杂的策略对整个布局树中进行分层。分层的好处在于,将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。滚动条、折叠上下文、transform、opacity等样式都会或多或少的影响分层结果,也可以通过will-change属性更大程度的影响分层结果
再下一步是绘制 主线程会为每个层单独产生绘制指令,用于描述这一层的内容该如何画出来
最后一个阶段就是画 GPU硬件完成最终屏幕成像
什么是reflow?
reflow本质就是重新计算layout树。 当进行了会影响布局树的操作后,需要重新计算布局树,会引发layout。为了避免连续的多次操作导致布局树反复计算,浏览器会合并这些操作,当JS代码全部完成后再进行统一计算。所以,改动属性造成的reflow是异步完成的。也同样因为如此,当JS获取布局属性时,就可能造成无法获取到最新的布局信息。浏览器再反复权衡下,最终决定获取属性立即reflow