阶段 - Parse
1、解析HTML,浏览器将从服务器获取到的HTML文件之后,会产生一个渲染任务,交给消息队列(EventLoop/MessageLoop)。
2、在事件循环机制的作用下,会将渲染任务交给主线程
3、主线程在获取到渲染任务之后,会先解析HTML文件内容,遇到CSS解析CSS文件,遇到script内容则会执行JS。CSS交给预解析器,预解析器会分担一点下载JS的任务,JS会阻塞主线程
4、预解析器(线程)会快速浏览HTML文件,发现外部的CSS文件后,会通过网络线程下载CSS文件,进行初步的解析,再将资源交给渲染主线程,所以CSS渲染不会阻塞渲染主线程
5、预解析器(线程)会快速浏览HTML文件,发现外部的JS文件后,会通过网络线程下载JS文件,并将资源交给渲染主线程去执行JS,在JS执行过程中,会阻塞主线程,因为JS代码可能会改动DOM和CSSOM
6、上述过程后会得到两棵树(DOM, CSSOM)
样式计算 - Computed-style
渲染主线程会遍历得到的DOM树,依次为树中的每个节点计算出它最终的样式。
这一过程中,很多预设值会变成绝对值,比如颜色会变成rgb(x,x,x),em,rem会变成px
完成之后就会得到一颗带有样式的DOM树(renderTree)
布局 - Layout
布局阶段会依次遍历DOM树的每一个节点,计算每个节点的几何信息,例如节点的宽度、相对包含块的位置。
大部分时候,DOM树和布局不是一一对应的。
比如display: none
的节点没有几何信息,不会生成到布局树;使用了伪元素选择器,虽然不出现在DOM中,但是拥有几何信息,会出现在布局树中。还有匿名行盒、匿名块盒等。
分层 - Layer
主线程会使用一套复杂的策略对整个布局树进行分层。
分层的好处,类似于局部刷新,只对有变动的图层进行修改,从而提升效率
例如:滚动条,堆叠上下文,transform,opacity等样式都或多或少的影响分层结果,也可以通过will-change属性更大程度的影响分层结果。可做为性能优化
绘制 - Paint
主线程会为每个层单独产生绘制指令集,用于描述如何将这些层的内容画出来。
完成绘制之后,主线程会将每个图层的绘制信息提交给合成线程,剩余工作由合成线程完成。
分块 - Tilinng
合成线程首先会对每个图层进行分块,将其划分为更多的小区域。
他会从线程池中拿出多个线程来完成分块工作。
光栅化 - Raster
合成线程会将块信息提交给GPU进程,并以极快的速度进行光栅化,。
GPU会开启多个线程进行光栅化,并优先处理靠近视口区域的块。
光栅化的结果就是一块一块的位图。
画 - Draw
合成线程拿到每个层、每个块的位图后,生成一个个quad(指引)信息
quad会标识出每个块应该画在屏幕上的哪个位置,以及会考虑到旋转缩放等变形
变形发生在合成线程,与渲染主线程无关,这就是transform效率高的原因。
合成线程会把quad提交给GPU进程,由GPU进程产生系统调用,提交给GPU硬件,完成最终的屏幕成像。
GPU加速
为了提高网页的渲染性能,现代浏览器还支持使用GPU进行渲染加速。GPU渲染可以将渲染树中的节点转换为GPU可识别的图形指令,然后交给GPU进行处理。GPU的并行计算能力可以大幅提高网页的渲染速度和流畅度。
总的来说,浏览器解析HTML到GPU渲染的过程是一个复杂的过程,需要涉及多个阶段和技术。对于网页开发者来说,理解这些过程能够帮助他们更好地优化网页性能,提高用户体验。
除了以上的技巧和优化,还可以考虑使用一些工具来辅助网页的开发和优化。例如,可以使用Chrome DevTools等开发工具来分析页面的性能和优化点。此外,还可以使用一些优化插件和库,如Lighthouse等,来自动化一些优化工作。
推荐像素的一生