浅谈浏览器渲染原理

Ryou Lv2

一个页面从输入 URL 到页面加载显示完成的过程?

这也算是面试中比较常见的一个问题,最近看了一些视频和资料之后多了一些理解。

所以趁着记忆理解还没消失,简单写一下自己的理解,以防不时之需。

解析 HTML

在浏览器输入地址按下回车之后,浏览器会向服务器发送请求,拿到 HTML 文档进行解析,生成 DOM(Document Object Model) 树 和 CSSDOM(CSS Object Model) 树。

由于一切渲染都是在主线程上进行的,为了提高效率,所以在解析 HTML 文档之前会启动一个 预解析线程 来下载外部 CSS文件 和 JS 文件,当主线程解析到 link 标签时,如果 CSS 文件还没下载解析好的话,主线程不会等待,而是继续解析后续的 HTML ,这就是 CSS 不会阻塞 HTML 解析的根本原因。

如果遇到了 script 标签,则会停止解析,等待 JS 文件下载好并将全局代码解析执行完成后才会继续解析 HTML ,这是因为 JS 代码的执行可能会修改当前的 DOM 树,所以 DOM 树的生成必须暂停,这就是 JS 会阻塞 HTML 解析的根本原因。

样式计算

主线程会遍历 DOM 树,依次为树种的每一个节点计算出它的最终样式,这一步完成后会得到一棵带有样式的 DOM 树。

布局

布局会依次遍历 DOM 树的每一个节点,计算每个节点的几何信息,比如节点的宽高。布局完成后会得到布局树。大部分情况 DOM 树和布局树并非一一对应的,比如 display: none; 的节点没有几何信息,因此不会生成布局树。

分层

主线程会使用一套复杂的策略对整个布局树进行分层。

分层的好处就在于将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。

在谷歌浏览器中可以通过 Layers 看到分层。

绘制

主线程会为每个层单独产生绘制指令,用于描述该层的内容该如何画出来。

渲染主线程的工作到此为止,剩余的步骤交给其它线程完成。

分块

主线程将绘制信息交给合成线程后,合成线程首先对每个图层进行分块,将其划分为更多的小区域,分块的工作时交给多个线程进行处理的。

光栅化

分块完成后进行光栅化。

合成线程会将块信息交给 GPU 进程,以极高的速度完成光栅化,并且优先处理靠近视口区域的块。

光栅化的结果就是一块块的位图(像素点信息)。

合成线程拿到每个层、每个块的位图后,生程指引信息。

指引会标出每个位图应该画到屏幕的哪个位置,以及会考虑到旋转、缩放等变形。

变形发生在合成线程,与渲染主线程无关,这就是 transform 效率高的本质原因。

通过上面一系列操作之后,合成线程会把指引信息交给 GPU 进程, GPU 进程会产生系统调用,提交给 GPU 硬件,最终将画面显示到屏幕上。

回流(reflow)、重绘(repaint)

reflow 的本质就是重新计算布局树,当进行了会影响布局树的操作后,需要重新计算布局树,比如修改元素的宽高、字体的大小等等,从而引发 reflow。

repaint 的本质就是重新根据分层信息计算了绘制指令,当改动了可见样式后,就需要重新计算,比如修改了某个元素的颜色,某个元素的背景等等、从而引发 repaint

完结
评论
此页目录
浅谈浏览器渲染原理