浏览器内核之渲染基础(上)

简介: 此文章是我最近在看的【WebKit 技术内幕】一书的一些理解和做的笔记。而【WebKit 技术内幕】是基于 WebKit 的 Chromium 项目的讲解。书接上文 浏览器内核之 CSS 解释器和样式布局本文剖析 WebKit 为网页渲染而构造的各种类型的内部结构表示,并介绍基本的网页软件渲染方式。WebKit的布局计算使用 RenderObject 树并保存计算结果到 RenderObject 树中。RenderObject 树同其他树(如 RenderLayer 树等),构成了 WebKit 渲染的为要基础设施。

前言


此文章是我最近在看的【WebKit 技术内幕】一书的一些理解和做的笔记。


而【WebKit 技术内幕】是基于 WebKit 的 Chromium 项目的讲解。


书接上文 浏览器内核之 CSS 解释器和样式布局


本文剖析 WebKit 为网页渲染而构造的各种类型的内部结构表示,并介绍基本的网页软件渲染方式。


WebKit的布局计算使用 RenderObject 树并保存计算结果到 RenderObject 树中。RenderObject 树同其他树(如 RenderLayer 树等),构成了 WebKit 渲染的为要基础设施。


1. RenderObject 树


1.1.1 RenderObject 基础类


为了解释本章内容,我使用以下基础的前端代码来说明。


<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
    <div>abc</div>
    <canvas id="webg1" width="80" height="80"></canvas>
    <a href="mailto:joe@example.com?subject=feedback">email me</a>
    <img src="" alt="">
    <input type="button" name="" />
    <select name="" multiple>
        <option value="">option</option>
    </select>
    <table>
        <tr>
            <td>data</td>
        </tr>
    </table>
    <script>
    var canvas = document.getElementById('webg1')
    var g1 = canvas.getContext('experimental-webg1')
    if (g1) {
        alert("There's no WebGl context available. ")
        return
    }
    </script>
</body>
</html>


上面代码经过 WebKit 解释之后,生成 DOM 树,也很容易想象到。在 DOM 树构建完成之后,WebKit 会为 DOM 树节点构建 RenderObject 树。请听我娓娓道来。


  • 不可视节点: 在 DOM 树中,该节点用户不可见,只是起到一些其他方面而不是显示内容的作用。如 “meta” 、“head”、“script” 节点等。
  • 可视节点: 在 DOM 树中,该节点用户可见,可以显示一块区域,如文字、图片、2D 图形等。


对这些 “可视节点”,因为 WebKit 需要将它们的内容绘制到最终的网页结果中,所以 WebKit 会为它们建立相应的 RenderObject 对象。


一个 RenderObject 对象保存了为绘制 DOM 节点所需要的各种信息,例如样式布局信息,经过 WebKit 的处理之后,RenderObject 对象知道如何绘制自己。


这些 RenderObject 对象同 DOM 的节点对象类似,它们也构成一棵树,在这里我们称这为 RenderObject 树。RenderObject 树是基于 DOM 树建立起来的一棵新树,是为了布局计算和渲染等机制而构建的一种新的内部表示。RenderObject 树节点和 DOM 节点不是一一对应关系。


是根据以下三条规则出发为 DOM 节点创建一个 RenderObject 对象的:

  • DOM 树的 document 节点。
  • DOM 树中的可视化节点,例如 html 、body、div 等。而 WebKit 不会为非可视化节点创建 RenderObject 节点。
  • 某些情况下 WebKit 需要建立匿名的 RenderObject 节点,该节点不对应于 DOM 树中的任何节点,而是 WebKit 处理上的需要,典型的例子就是匿名的 RenderBlock 节点。


WebKit 处理影子 DOM 没有什么特别的不同,虽然 JavaScript 代码没法访问影子 DOM ,但是 WebKit 需要创建并渲染 RenderObject。


WebKit 在创建 DOM 树的同时也创建 RenderObject 对象。如果 DOM 树被动态加入了新节点,WebKit 也可能创建相应的 RenderObject 对象。


微信图片_20220512124904.png


每个 Element 对象都会递归调用 “attach” 函数,该函数检查 Element 对象是否需要创建 RenderObject 对象,如果需要,该函数会使用 NodeRenderingContext 类来根据 DOM 节点的类型来创建对应的 RenderObject 节点。


DOM 树中,元素节点包含很多类型。同 DOM 树一样,RenderObject 树中的节点也有很多类型。


微信图片_20220512124918.png


而图中间的 RenderObject 类还包含了 RenderObject 的主要虚函数,还可以分为以下 5 类:


微信图片_20220512124931.png


微信图片_20220512124947.png


1.2 RenderObject 树


RenderObject 对象构成了一棵树。RenderObject 树的创建过程主要是由 NodeRenderingContext 类来负责。


微信图片_20220512124958.png


思路:首先 WebKit 检查该 DOM 节点是否需要创建 RenderObject 对象。如果需要,WebKit 建立或者获取一个创建 RenderObject 对象的 NodeRenderingContext 对象,NodeRenderingContext 对象会分析需要创建的 RenderObject 对象的父亲节点、兄弟节点等,设置这些信息后完成插入树的动作。


根据上面的代码生成图 7-4 所示的 DOM 树和 RenderObject 树。


微信图片_20220512125007.png


上图中使用虚线箭头表示两种树的节点对应关系,其中 HTMLDocument 节点对应 RenderView 节点,RenderView 节点是 RenderObject 树的根节点。另外,WebKit 没有 HTMLHeadElement 节点(非可视化元素),因为没有被创建 RenderObject 子类的对象。


1.2 网页层次和 RenderLayer 树


1.2.1 层次和 RenderLayer 对象


网页是有层次结构的,可以分层的,一是为了方便网页开发者开发网页并设置网页的层次,二是为了 WebKit 处理上的便利,为了简化渲染的逻辑。


WebKit 会为网页的层次创建相应的 RenderLayer 对象。当某些类型RenderLayer 的节点或者具有某些 CSS 样式的 RenderLayer 节点出现的时候,WebKit 就会为这些节点创建 RenderLayer 对象。一般来说,某个 RenderObject 节点的后代都属于该节点,除非 WebKit 根据规则为某个后代 RenderObject 节点创建了一个新的 RenderLayer 对象。

RenderLayer 树是基于 RenderObject 树建立起来的一棵新树。


而且有结论:RenderLayer 节点和 RenderObject 节点不是一一对应关系,而是一对多的关系。


RenderObject 节点需要建立新的 RenderLayer 节点,是根据以下基本规则:

  • DOM 树的 Document 节点对应的 RenderView 节点。
  • DOM 树中的 Document 的子女节点,也就是 HTML 节点对应的 RenderBlock 节点。
  • 显式的指定 CSS 位置的 RenderObject 节点。
  • 有透明效果的 RenderObject 节点。
  • 节点有溢出(Overflow)、alpha 或者反射等效果的 RenderObject 节点。
  • 使用 Canvas 2D 和 3D(WebGl)技术的 RenderObject 节点。
  • Video 节点对应的 RenderObject 节点。

除了根节点也就是 RenderLayer 节点,一个 RenderLayer 节点的父亲就是该RenderLayer 节点对应的 RenderObject 节点的祖先链中最近的祖先,并且祖先所在的RenderLayer 节点同该节点的 RenderLayer 节点不同。基于这一原理,这些 RenderLayer 节点也构成了一棵 RenderLayer 树。


每个 RenderLayer 节点包含的 RenderObject 节点其实是一棵 RenderLayer 子树。 理想情况下,每个 RenderLayer 对象都会有一个后端类,该后端类用来存储该 RenderLayer 对象绘制的结果。实际情况中则比较复杂,在不同的渲染模式下,不同 WebKit 的移植中,情况都不一样。RenderLayer 节点的使用可以有效地减少网页结构的复杂程度,并在很多情况下能够减少重新渲染的开销。


在 WebKit 创建 RenderObject 树之后,WebKit 也会创建 RenderLayer 树。当然,某些 RenderLayer 节点也有可能在执行 JavaScript 代码时或者更新页面的样式被创建。同 RenderObject 类不同的是,RenderLayer 类没有子类,它表示的是网页的一个层次,并没有 “子层次” 的说法。


1.2.2 构建 RenderLayer 树


构建 RenderLayer 树的过程非常简单,甚至比构建 RenderObject 树还要简单。根据前面所述的条件来判断一个 RenderObject 节点是否需要建立一个新的 RenderLayer 对象,并设置 RenderLayer 对象的父亲和兄弟关系即可。

根据刚开始的代码,WebKit 中的 RenderObject 树表示如图 7-5 左边所示的结构。右边描述是就是 WebKit 所生成的对应的 RenderLayer 树。根据 RenderLayer 对象创建的条件来看,该示例代码的 RenderLayer 树应该包含三个 RenderLayer 节点——根节点和它的子女,以及叶子节点。


微信图片_20220512125033.png


根据上面最初的代码,生成 图 7-6 ,表示 WebKit 内部表示的具体结构 RenderObject 树、RenderLayer 树和布局信息的中大小和位置信息。


微信图片_20220512125048.png


首先,图中 ‘layer at(x,x)’ 表示的是不同的 RenderLayer 节点,下面所有 RenderObject 子类的对象均属于该 RenderLayer 对象。


以第一个 RenderLayer 节点为例,它对应于 DOM 树中的 Document 节点。后面的 “(0,0)” 表示该节点在网页坐标系中的位置,最后的 “1028X683” 表示该节点的大小,第一层包含的 RenderView 节点后面的信息也是同样的意思。


其次,看第二个 layer ,其包含了 HTML 中的绝大部分元素。这里面有三点需要解释一下:

  • 一,“head” 元素没有相应的 RenderObject 对象,因为 “head” 是一个不可视的元素;
  • 二,“canvas” 元素并不在第二个 layer 中,而是在第三个 layer(RenderHTMLCanvas)中,虽然该元素仍然是 RenderBody 节点的子女;
  • 三,该 layer 层中包含一个匿名(Anonymous)的 RenderBlock 节点,该匿名节点包含了 RenderText 和 RenderLnline 等子节点。


再次,第三个 layer 层,因为 JavaScript 代码为 “canvas” 元素创建了一个 WebGl 的 3D 绘图上下文对象,WebKit 需要重新生成一个新的 RenderLayer 对象。


最后,来说明一下三个层次的创建时间。在创建 DOM 树之后,WebKit 会接着创建第一个和第二个 layer 层。但是,第三个 RenderLayer 对象是在 WebKit 执行 JavaScript 代码时才被创建的,这是因为 WebKit 需要检查出 JavaScript 代码是否为 “canvas” 确实创建了 3D 绘图上下文,而不是在遇到 ”canvas“ 元素时创建新的 RenderLayer 对象。



相关文章
|
2月前
|
消息中间件 JavaScript 前端开发
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
|
3月前
|
前端开发 JavaScript 数据可视化
探索浏览器的内心世界:渲染机制的奥秘
探索浏览器的内心世界:渲染机制的奥秘
探索浏览器的内心世界:渲染机制的奥秘
|
3月前
|
消息中间件 前端开发 Java
【面试题】前端必修-浏览器的渲染原理
【面试题】前端必修-浏览器的渲染原理
|
7月前
|
Web App开发 存储 监控
浏览器基础原理-安全: 渲染进程-安全沙盒
浏览器基础原理-安全: 渲染进程-安全沙盒
37 0
|
9月前
|
开发框架 安全 .NET
您是否也有想在浏览器中实时的编辑代码并且渲染的想法?
您是否也有想在浏览器中实时的编辑代码并且渲染的想法?
52 0
|
9月前
|
Web App开发 自然语言处理 JavaScript
JS进阶(五)同步异步编程及浏览器的底层渲染机制
浏览器渲染机制 浏览器底层渲染机制 一个页面从服务器访问,拿到页面源代码之后做的事情是什么? 生成Dom树(DOM Tree) => 对HTML文件的处理 基于HTML获取的是流文件 (进制编码) 把进制编码编译为具体的字符 按照令牌TOKEN进行解析 (分词/断词) 生成具体的节点 (元素标签/文本节点....) 按照相互的依赖关系生成一个DOM树 (节点树)
133 0
|
11月前
|
存储 缓存 前端开发
|
11月前
|
Web App开发 JavaScript 算法
深入解析 EventLoop 和浏览器渲染、帧动画、空闲回调的关系
关于 Event Loop 的文章很多,但是有很多只是在讲「宏任务」、「微任务」
|
Web App开发 缓存 移动开发
浏览器渲染原理
浏览器渲染原理
浏览器渲染原理
|
Web App开发 监控 安全
浏览器原理 36 # 浏览上下文组:如何计算Chrome中渲染进程的个数?
浏览器原理 36 # 浏览上下文组:如何计算Chrome中渲染进程的个数?
88 0
浏览器原理 36 # 浏览上下文组:如何计算Chrome中渲染进程的个数?