浏览器是如何渲染HTML和CSS的?

content 的概念:

在网页的内部或者是 Web 应用程序前端的所有代码都称为 content,包括 HTML、CSS、JS 等。

将像素显示在屏幕中的是底层的操作系统提供的图形库来完成的。目前的话,大部分平台上有一个叫 OpenGL 的 API 标准,Windows 中的话还需要额外的转换成 Direct X 的。

也就是说,浏览器会把content中的内容,例如 HTML、CSS、JS 等转换成 OpenGL 能够调用的 API,最后绘制在屏幕中。

HTML的解析

在浏览器的第一步中获取到的是 HTML,这是一种以流的方式传进来的,当获取到 HTML 后,HTML 的解析器会把 HTML 解析成一棵 DOM (Document Object Model)树,后续的 CSS 或者是其他的资源是直接或者是间接的依附在 DOM 树上的,也就是说,DOM 树是整个渲染的基础。

DOM 的作用有两个,一个是作为页面的内部展示,另一个是将查询或者是修改渲染的 API 暴露给 JS 的引擎系统,例如 V8 JS 引擎,会把真实的 DOM 树包装过后,再对外暴露出 DOM 的 Web API,Web API 就可以被底层的图形库来调用了。

CSS 的解析

第二步:CSS 的解析

首先 CSS 是有选择器和属性的,CSS的选择器的选择范围是在 DOM 树中的 Node 节点集中。

在渲染中,CSS Parser 会把 CSS 的文本解析成各种样式规则的对象模型,这些对象模型可以被各种方式索引,方便查找。

接着就是怎么把样式应用于对应的 DOM 树中?首先是从文档的样式表中,获取到所有已经解析完成的样式,其中包括了浏览器提供的一组默认的样式,然后计算出每个 DOM 元素样式属性的最终值,存储在 ComputedStyle(计算样式)对象模型中。这就是一个样式属性和值映射的超大的 Map。ComputedStyle 会挂载好最终计算出来的样式。,这就是样式引擎的输出。

布局

布局通常会为单个图层对象计算多种边界矩形,计算出每一个图形的边界值。布局的过程中,有一些元素的样式比较复杂,但是在 DOM 结构中和各种样式的计算值中,都是把上一个阶段的输出当做下一个阶段的输入,所有的布局信息都是存储在一棵与 DOM 相关联的树结构中的。我们称为布局树。布局树中的每一个节点都是一个 LayoutObject 对象。

在 DOM 中,布局树和 DOM 的节点几乎是一一对应的,有一些例外是,如果给一些节点设置了 display:none,那么这样的就不会创建布局对象。有些情况是还可能存在没有节点的布局对象。也有可能是,一个节点下不止一个布局对象。所以我们会默认认为,DOM 对象和布局对象是有直接对应关系的。

布局对象会在渲染时遍历一遍 DOM 树,完成关于元素的几何位置确定等工作。

绘制

完成上面的三个动作后,接下来就是绘制的工作了。

绘制的工作过程是,先把把所有的绘制操作都会记录在一张待显示的列表中,如果有些元素在重叠的时候让它们正常的堆叠起来,绘制的时候是按照层叠的顺序来绘制的,根据的是 z-index 的属性,不仅仅只是按照 DOM 中的顺序。

栅格化

列表中的各个待显示的绘制操作是由栅格化进程执行的。 栅格化得过程是将列表中的全部或者部分待显示页转化为颜色值的位图。位图中的每个单元格记录四通道色值。栅栏位图保存在内存中,通常是由 OpenGl 纹理对象。

GPU 完成的动作,不仅仅是保存未绘制的位图,还会执行生成位图的命令。这个过程叫做快速栅格化或者是 GPU 栅格化。

目前的这个阶段还只是存在于内存中。这里,会将每一个像素点的颜色生成一个 32 位的二进制码,分别表示4种颜色,同时,我们可以利用 GPU 渲染加速。需要 GPU 的进程来辅助的话,有两个原因,第一个是需要绕过渲染器沙箱,第二个是 OpenGL 使用的图形驱动程序经常不太稳定或者是有安全的漏洞。用 GPU 进程去隔离掉 GL。

GPU的进程

这个阶段就是真实的渲染过程。再通过 skia 调用的 openGL API,通过 CommandBuffer 调用 GPU 进程,进行真实的渲染。

总结

以上就是浏览器完成了一个像素的渲染过程。

参考资料

参考资料是:

Life of a Pixel : 像素的一生

how-the-browser-renders-html-css

spacedong wechat
愿意交个朋友吗~
觉得有收获么