(翻译)Web 浏览器如何一步步工作3 — 渲染阶段

在上一篇文章中,我们看到了导航阶段。三个流程协同工作以交付下一阶段的文档。 在这篇文章中,我们将打开渲染阶段的盒子,看看里面有什么。

从文档到网页 --- 8 个子阶段

渲染阶段包括8个子阶段:

  1. DOM构建 ( DOM construction )
  2. 样式计算 ( Style computation )
  3. 布局 ( Layout )
  4. 层 ( Layer )
  5. 画 ( Paint )
  6. 分块 ( Tiling )
  7. 光栅化 ( Raster )
  8. 绘制显示 ( Draw Quad and Display )

渲染器进程中的两个线程涉及:

  • 主线程负责 1-5 个阶段
  • 合成器线程管理 6-8 个阶段

第一阶段:DOM构建

输入是从网络过程中传递过来的 HTML 文档,输出是 DOM 树。 为什么是 DOM 树?浏览器不支持 HTML。渲染器进程首先需要将 HTML 文档"翻译"为浏览器可以理解的内容。 那就是 DOM 树。 让我们看一个例子。

html 复制代码
<html><body>
  <h1>Hello world!</h1>
  <div>
    <p>
      It is a message from
      <span>the web</span>
    </p>
  </div>
</body></html>

该示例的 DOM 树如图所示。 DOM 树是 HTML 代码的表示。我们可以通过在 Chrome 控制台中输入"文档"来访问它。

DOM 树存在于内存中。因此,JavaScript 可以访问并编辑它。

第 2 阶段:风格计算

输入是 CSS 样式,输出是计算的样式结构。 它分为三个步骤。

第 1 步:"翻译"CSS

与 HTML 类似,浏览器不支持 CSS。翻译的结果是样式表。 在Chrome浏览器控制台中输入"document.styleSheets",我们可以看到所有解析的样式表。 这些样式表来自

  • 标签中链接的来源,
  • 内的样式,以及
  • 内联样式

与 DOM 树一样,样式表看起来像 JavaScript 对象结构,可以在内存中访问和编辑。

我们以Medium主页为例。它有 11 个样式表。第一个来自 标记,其余的都是 标记内的样式。

步骤 2:标准化值和统一

我们在 CSS 中使用各种值和单位。

  • 宽度:50%
  • 填充:2em 0
  • 字体大小:1rem

它们是相对值。 在此步骤中,所有相对值都将转换为绝对值(像素)。 为什么是像素?在渲染阶段结束时,浏览器在屏幕上显示位图。位图由像素组成。 是时候做一下我们的数学计算了。 标准化结束后,之前的 CSS 值更改为以下内容:

  • width: 500px (假设其父元素的宽度为1000px。)
  • padding: 32px 0 (假设元素的字体大小为16px。)
  • font-size: 16px (假设根元素的字体大小为16px。)

第三步:样式计算

最后,渲染器进程计算计算出的样式并将它们附加到每个元素。 为什么要计算最终样式 ( computed style )? CSS 代表层叠样式表。级联意味着一个元素从其祖先继承一些样式,并用自己的样式覆盖其中的一部分。 渲染器进程根据级联规则计算所有样式。然后它为每个元素生成最终计算样式的列表。 例如,一个元素从其祖先继承 font-size: 16px。本身有一个font-size:32px。计算出的 font-size 样式为 font-size: 32px。

该图显示了 Medium 主页上 h2 元素的计算样式列表。 计算完成后,最终结果是计算出的样式列表。我们可以在Chrome控制台中查看一下。

第三阶段:布局 ( Layout )

在此阶段,输入是 DOM 树和计算样式。输出是带有计算的布局信息的布局树。

步骤一:构建布局树 ( Layout Tree )

布局树看起来就像 DOM 树的复制品。 有什么区别?首先,DOM 树中"不可见"的元素不会包含在布局树中。 例如,具有 display: none 的元素会从布局树中排除。与
标记内的所有元素相同。有一个例外:可见性为隐藏的元素;位于布局树中。 其次,一些 CSS 属性将内容添加到布局中。 以伪类为例,div::after {content: 'I'm here';} 创建了一个包含在布局树中的内容,尽管它不存在于 DOM 树中。

步骤2:计算几何信息

要在空白画布上画一个盒子,我们需要知道:

  • 起始 x、y 坐标,以及
  • 盒子的尺寸

布局树中的每个元素都是一个盒子。 计算每个元素的最终几何形状是一项艰巨的工作。想一想。在 CSS 中,许多属性都会修改元素的几何形状。

  • float: left
  • width: 100px
  • display: absolute
  • ...

设计一个有效的布局系统来使用它是具有挑战性的。如果您有兴趣,BlinkOn 的开发人员与我们分享了一些知识。 主线程为我们完成繁重的工作并完成所有的计算。 现在它有一个布局树。在布局树上,每个元素都有精确的坐标和大小信息。

第四阶段:层 ( Layer )

输入是布局树,输出是图层树。 第三层是关于层的。 我们都喜欢 CSS 中的 3D 变换效果和方便的 z-index 属性。它们都与图层相关。 如果您曾经使用过 Photoshop 或 Sketch,您就会对图层非常熟悉。浏览器中的层具有相同的概念。 同时,图层树与绘画顺序有关。为什么我们要关心绘画顺序? 例如,您有两个重叠元素,元素 A 和元素 B。A 的 z-index: 9,比没有 z-index 属性的 B 更重要。 A 将显示在 B 的顶部。渲染器进程首先在画布上绘制 B,然后在其顶部绘制 A。这是一个绘画订单。 绘制顺序对于网页的准确显示至关重要。 图层树是什么样子的?

从图中我们可以看到并非所有元素都有自己的图层。绘制层价格昂贵。为了获得更好的性能,渲染器进程仅在需要时创建图层。 什么时候需要?渲染器进程考虑两种元素。

具有堆叠上下文的元素

  • z-index
  • position: absolute
  • transform
  • will-change

以上是一些与堆叠上下文相关的 CSS 属性。渲染器进程为具有任何这些属性的元素创建一个层。 您可以在 MDN 文档上查看影响图层树的 CSS 属性的完整列表。 Chrome 允许我们查看网页上的图层。

剪裁的元素

剪切元素的典型情况是溢出的文本元素。 假设我们有一个 300px * 300px 的 div。 div 框中的长文本溢出。当给盒子溢出时:auto;我们知道文本在 div 内可以滚动。 当看到溢出的文本元素时,渲染器进程会创建三层。

  • 一种用于剪切文本,在 300px * 300px 区域中可见。
  • 其中一个用于全文内容,以便您可以滚动浏览它们。
  • 一个用于滚动条

在控制台中查看更容易理解这个机制。 在 Chrome 中运行以下 HTML。

html 复制代码
<html>
  <body>
    <h1>Hell world!</h1>
      <div class="box" style="width: 300px; height: 300px; overflow: auto;">
          <p>Lorem ipsum vel viverra elementum ut et parturient placerat curae at vestibulum ullamcorper a ullamcorper mattis nascetur ullamcorper ut mollis rhoncus sed parturient in vestibulum vestibulum. Leo nec nisi nec erat a purus aliquam habitasse a sem id nisi ullamcorper viverra suspendisse mollis fringilla a dis a suspendisse parturient a. A commodo scelerisque mi dictum non orci suspendisse felis velit dapibus a sit facilisis velit mi libero ultrices leo consectetur tempus montes quis consequat condimentum a ullamcorper amet. Donec orci fames id consectetur nascetur parturient et magnis nunc lacinia aliquet nec dolor dictumst conubia a hac nibh consectetur habitant vel a cras.</p>
      </div>
  </body>
</html>

打开图层面板看一下。图层显示在图层面板中。尝试滚动框中的文本,您就会明白。

最后,在完成所有计算之后,渲染器进程手头上就有了图层树。

第五阶段:绘制 ( Paint )

在此阶段,输入是布局树和图层树,输出是绘制记录。 绘制记录是什么样的? 它由三个部分组成:

  • Action
  • 位置,包括坐标(x,y)和尺寸(宽度,高度)。
  • 样式

例如,绘制记录如下所示:

  • Action: Draw Rect
  • Pos: 0, 0, 300, 300
  • backgroundColor: red

在(0, 0)处绘制一个300px * 300px的红色矩形是一个绘制记录。 绘画记录就像浏览器执行绘画的注释。 绘画记录是这些注释的列表,按照我们在图层树中确认的顺序排列,例如"首先是背景,然后是矩形,然后是文本"。 在下一步中,合成器线程继承主线程的工作,开始根据绘制记录生成位图。

第六阶段:分块 ( Tiling )

输入是绘制记录和图层,输出是图块。 平铺时,图层被分成图块,浏览器根据视口位置优先进行渲染。 这里有三个关键词:layer、tile、viewport。 我们以 Medium 主页为例。从 DevTools 的 Layer 面板中,我们可以看出它分为三层:

  • document
  • header
  • scroll bar

"文档"层被分成多个图块。通常,图块尺寸为 256px * 256px 或 512px * 512px。

渲染所有图块的成本很高。当前位于视口中的可见图块具有优先权。

第 7 阶段:光栅化 ( Raster )

输入是图块,输出是位图 ( bitmaps )。 了解图块后,合成器线程创建一个光栅线程池。多个光栅线程同时进行光栅图块。

为了加速该过程,光栅线程通过 IPC 将图块发送到 GPU 进程。然后 GPU 进程从图块生成位图并将位图保存在 GPU 内存中。 当位图准备好后,GPU 进程将它们传递回渲染进程中的合成器线程以进行下一步。

第 8 阶段:绘制显示 ( Draw Quad and Display )

输入是位图,输出是合成器帧。 当处理完所有需要的图块后,合成器线程会向浏览器进程发送一个名为"绘制四边形"的命令。 在浏览器进程内部,"viz"组件接收"绘制四边形"命令,执行"显示合成器"命令,并将合成器帧"绘制"到我们的计算机内存中。

最后,浏览器进程在浏览器中显示合成器框架。 令人印象深刻的是,在现代浏览器中,整个 8 个阶段都在 1/60 秒内发生。即 16.67 毫秒。

要点

渲染的8个阶段:

  1. 渲染器进程中的主线程将 HTML 文档"翻译"为 DOM 树
  2. 它将 CSS 传递到计算样式中
  3. 它基于 DOM 树和计算样式创建布局树
  4. 从布局树中,它生成图层树
  5. 最后,建立绘制记录
  6. 合成器线程继承绘制记录,根据当前视口开始耕种
  7. 多个光栅线程在 GPU 进程的帮助下将光栅图块转换为位图。
  8. 浏览器进程接收来自浏览器进程的Draw Quad命令,然后它在我们面前显示页面的一个框架。

如果您听说过渲染树,那么它在 2019 年就不再相关了。这里类似的概念是图层树。

参考

相关推荐
程序员凡尘17 分钟前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
(⊙o⊙)~哦6 小时前
JavaScript substring() 方法
前端
无心使然云中漫步6 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者6 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_7 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js
麒麟而非淇淋8 小时前
AJAX 入门 day1
前端·javascript·ajax
2401_858120538 小时前
深入理解MATLAB中的事件处理机制
前端·javascript·matlab
阿树梢8 小时前
【Vue】VueRouter路由
前端·javascript·vue.js
随笔写9 小时前
vue使用关于speak-tss插件的详细介绍
前端·javascript·vue.js