浏览器渲染原理:全流程

88 阅读6分钟

渲染流程就像一个流水线,每一步的输出都是下一步的输入。

渲染流程如何开始

从用户输入一个url开始,浏览器通过网络线程向服务端请求页面,服务端向浏览器返回一个html页面。

被解析后的html页面通过网络线程送入 消息队列,等待 渲染主线程的执行

渲染流程

解析HTML

以下过程在 网络线程中完成:

由于服务器以 字节流的方式传输html页面,浏览器获得服务器的响应时,并不认识html页面中的内容。于是,浏览器需要将字节流转化为字符串。但是,浏览器仍然不认识字符串里的内容,因此,还需要进行进行 标记化,即解析出html标签、属性以及文本内容,并进行结构化、语义化,形成一个 标记流。这为构建DOM Tree提供了基础。

总结一下,为了构建DOM Tree,浏览器做了以下准备:

  1. 将字节流转解码为字符串
  2. 将字符串进行标记化,形成一个标记流,使得浏览器识别html页面的结构

构建Dom Tree

渲染主线程通过事件循环,将html页面从消息队列中取出。开始进行真正的渲染。

解析一个个DOM元素,构建出DOM Tree,形如:

外部资源

当遇到了外部资源怎么办?例如

渲染主线程开启时,浏览器还会开启另一个线程,称为 预解析线程,该线程用于扫描html页面,寻找外部资源。当遇到外部资源时,开启 网络线程,向服务器请求相应的外部资源。

解析js
  • 如果外部资源为js文件

渲染主线程被阻塞,转而去执行预解析线程通过网络线程下载的js文件。为什么要阻塞渲染主线程呢?因为js可能会直接操作DOM元素,这样,就影响了DOM Tree的结构。因此,需要暂停主线程中对DOM Tree的创建,直到执行完js。然而,这样就使得浏览器渲染速度变慢,所以建议将

构建CSSOM
  • 如果外部资源是css文件

预解析线程完成CSS解析。CSS 的解析在经历了从字节数据、字符串、标记化后,最终也会形成一颗 CSSOM (CSS Object Model/CSS对象模型),形如:

注意:CSSOM构建并不会阻塞DOM Tree的构建过程。此时,渲染主线程和预解析线程并行执行。

构建Render Tree(渲染树)

当DOM Tree和CSSOM被构建完成后,渲染主线程将两颗树合成为一颗树,称为Render Tree。渲染树包含了所有占用布局空间的结点和样式信息。形如:

注意:

在构建Render Tree时,CSS文件可能还未被完全解析。当预解析线程构建CSSOM时,要阻塞Render Tree的构建,即阻塞渲染主线程。因为Render Tree需要CSSOM。

问题:Render Tree和CSSOM有什么区别

有些结点不可见,比如设置了display: none,这样的结点不会出现在Render Tree中,会出现在CSSOM中。

但是,要注意 visibility:hidden 的节点仍然占用布局空间,只是不可见。所以仍然在Render Tree中。

布局:构建Layout Tree(布局树)

虽然已经构建出了Render Tree,但是,Render Tree忽视了元素的几何信息,包括宽度、高度、位置信息。于是,渲染主线程遍历Render Tree通过样式计算,构建出Layout Tree(布局树)。

分层:构建Layer Tree(层次树)

主线程遍历整颗布局树,构建出Layer Tree。下面是网页的分层结构:

这样做的好处是:当单个dom元素被修改时,浏览器只需重新处理该元素所在的层,而不是整个页面,这样大大提高了渲染速度。

生成绘制指令

主线程为每一个层生成一系列的绘制指令。这些绘制指令描述了如何在屏幕上绘制各种图形和文字。接下来,这些绘制指令交给 合成线程。渲染主线程的工作就告一段落了。

分块

  • 合成线程完成分块的操作,该线程会从线程池调用多个线程帮助来帮他完成分块工作。
  • 分块是将每个图层分成一个个小块。

光栅化

  • GPU线程完成:当分块完成后,合成线程将所有的块信息交给GPU线程

什么是光栅化

将每个块转化成位图图像,位图由一个个像素点组成,每个像素点都有自己的颜色,用于描述图像内容。

绘制

  1. 光栅化完成后,合成线程拿到每个块的位图信息,接着生成一个个 指引。这些指引标识出每个位图应该绘制在屏幕的哪个位置。
  2. 合成线程向浏览器提交一个个渲染帧,以形成合成帧。
  3. 合成帧交给 GPU线程,由该线程完成最后的成像。

注意

  1. trasform发生在绘制阶段,这就是 transform 属性能够高效执行的根本原因。
  2. 当页面滚动的事件时,合成线程会将合成帧发送给 GPU,这样可以快速更新页面,而不需要经过完整的渲染流程。

回流和重制

回流(reflow)

是什么

重新构建布局树,是一个很昂贵的操作。

引发回流的事件

  1. 删除/添加DOM元素
  2. 改变DOM元素的尺寸/位置
  3. 浏览器窗口发生变化

优化

  1. 当js对DOM元素的布局进行操作时,js会合并这些操作,进行统一计算。这样,就减少了回流次数。不过,由于这个异步操作,通过js获得dom元素的布局属性时,可能无法得到最新值。

重制(repaint)

是什么

改变了可见样式之后,需要重新生成绘制指令。

注意:虽然布局树包含了所有可见元素的样式信息,但不意味着重制会改变布局树。重绘只会根据分层,重新计算绘制指令。

优化

  1. 利用第三方库统一样式,减少样式修改
  2. 针对性重制