浏览器是个极其复杂的程序,这里只是挑几个和前端息息相关的重要内容来说
在学习如何渲染之前需要知道一个浏览器浏览器会有多个进程,其中主要进程有浏览器进程,网络进程,渲染进程这里我们主要学习内容就发生在渲染进程。当渲染进程启动后会开启一个渲染主线程用来执行HTML,CSS,JS代码,每打开一个新标签页都会新加一个渲染主线程来保证每个标签页互不干扰。
渲染时间点
当网络线程接收到一个HTML后会生成一个渲染任务放到消息队列排队,在事件轮询的机制下,渲染主线程会取出渲染任务执行,至此渲染流程开始。
渲染流水线
渲染流程又分为多个阶段解析HTML,计算样式,布局,分层,绘制,分块,光栅化,画。
每个阶段都有明确的输入和输出,上一步的输出会作为下一步的输入,这样环环相扣就形成了一条完整的渲染流水线。
解析HTML
渲染的第一步是解析 HTML来生成dom,cssom树,生成树的原因是为了方便后续操作,不然一直在HTML字符串取值是一个很痛苦的事情。
解析过程中遇到js执行js,遇到css解析css。为了提高效率浏览器会开启一个预解析线程,率先下载HTML中的外部css和js文件。
解析到link位置时css文件还没下载好主线程不会等待,继续解析后面的HTML这是因为css下载是在预解析线程完成的,这也是css不会阻塞HTML解析的根本原因。
解析到script位置时,会停止解析HTML转而去等待js文件下载完成,且全局解析执行完成后,才会继续解析HTML。因为js在执行过程中会改变DOM树,所以 DOM 树的生成必须暂停。这就是js会阻塞HTML解析的根本原因
样式计算
主线程会遍历得到的DOM树,依次为树的每个节点计算出最终样式,也叫做computed style。
在这一过程中,会把预设值变为绝对值,比如red转换为rgb(255,0,0),em转换为px等,最后会得到一个带有样式的DOM树。
布局
主线程会依次遍历DOM树,计算出每一个节点的几何信息列如宽高,相对于包含块的位置等等。
之后浏览器会用layout树来保存这些几何信息(layout树并不是js里的树是浏览器内部的)。往往DOM树和layout树并不一一对应,比如display:none;的节点就不会有任何几何信息,又比如使用伪类,虽然DOM树没有这些节点,但是这些节点是有几何信息的,还有匿名行盒,块盒这些都会导致DOM树和layout树并不一一对应。
分层
主线程使用一套复杂的逻辑进行分层。
这样带来的好处就是,将来某一层内容变化后只用改变当前层,从而提高效率。
滚动条,堆叠上下文,opacity 等都可以影响分层,也可以使用will-change更大程度的影响分层结果。
使用chrome浏览器f12点击图层就可以看到当前页面的分层
绘制
主线程会为每一层生成指令集,用于描述这一层如何画出来。
分块
绘制完成后,接下来会交给合成线程来处理,合成线程会把每一层进行分块处理,分成更小的区域,
分块过程会启用多个线程来处理。
光栅化
接下来会交给GPU进程处理,优先处理靠近视图窗口的块,处理后的结果就是一个个位图。(此过程会⽤到 GPU 加速
)
画
合成线程拿到每个块后会生成一个个 quad(指引)信息。
指引会标记出每个位图应该画到屏幕的某个位置,还会考虑缩放,旋转,变形等。
变形发生在合成线程说于主线程无关,之所以transform效率高也是因为于此。
合成线程会把quad交给GPU进程,再由GPU进程交给GPU硬件处理,最后画到了屏幕上。
完整过程
网络线程接收到一个HTML会添加一个渲染任务到消息队列,后经过事件轮询主线程会取出执行渲染任务开启渲染流程,渲染流程分为多个阶段
解析HTML:解析css,执行js生成DOM,CSSOMS树。
计算样式:遍历DOM树计算出最终样式,生成一个带有样式的DOM树。
布局:遍历DOM树生成几何信息的layout树,layout树和DOM树通常不一一对应。
分层:为了提高效率使用一套复杂的策略分层,滚动条,transform都会影响分层,will-change会更大程度的影响分层结果。
绘制:为每一层生成指令集,用于描述如何画出它。
到这里主线程已处理完毕接下来交给其他线程处理
分块:接下来由合成线程会使用多个线程,把每一层分成更小的区域。
光栅化:会使用GPU进程把每个块生成位图。
画:合成线程拿到块层后生成一个个指引之后交给GPU线程,GPU线程产生系统调用,交给GPU硬件显示到屏幕上。
面试题
看完以上浏览器是渲染页面的全流程,那么下面的几道经典面试题也就迎刃而解了。不懂得可以多看几遍流程加深理解。
什么是 reflow(回流)?
回流的本质就是重新计算layout树 ,浏览器为了尽可能减少reflow和repaint操作,会合并这些操作等到js全部执行完成后再去重新计算,所以回流改变是异步的。
那么什么时候会产生同步的layout呢?
当 JS 获取布局属性时,比如getComputedStyle()或currentStyle、offsetTop、offsetLeft、offsetWidth、offsetHeight等等。因为异步会导致获取到错误的值,浏览器在反复权衡下,最终决定获取属性立即 reflow。
什么是 repaint(重绘)?
repaint的本质就是根据分层信息计算了绘制指令。
当改动了可见样式后就会触发重绘。
由于布局样式也是可见样式所以reflow一定会触发repaint。
为什么transform效率高
因为transform不涉及到reflow和repaint,它只影响最后一个画的阶段所以效率高。
由于画(draw)阶段在合成线程中所以几乎不影响主线程,反之主线程哪怕在忙也不会影响到transform的变化。
有问题的地方还请大佬及时指出我会及时修改谢谢