(翻译)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 年就不再相关了。这里类似的概念是图层树。

参考

相关推荐
王解39 分钟前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁43 分钟前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新5 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html
秦jh_6 小时前
【Linux】多线程(概念,控制)
linux·运维·前端
蜗牛快跑2136 小时前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy6 小时前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js