likes
comments
collection
share

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

作者站长头像
站长
· 阅读数 26

前言

渲染流程的本质是将HTML、CSS、JavaScript这些内容整合在一起,等到给用户查看的图像。

我们先来看这三者的含义,如下图:

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

  • HTML:由标记和文本组成。标记也叫做标签,每个标签都有自己的语义,浏览器会根据标签的语义来正确展示HTML内容。比如<p>标签表示段落,<h1>标签表示一级标题。
  • CSS又称为叠层样式表(Cascading Style Sheets),是由选择器和标签组成。比如图中的p选择器,会把HTML里面p标签的内容选择出来,再把选择器的属性值应用到<p>标签的内容上。
  • JavaScript,提供了修改页面信息的功能,可以让网页内容产生变化。

渲染的机制是复杂的,所以渲染模块在执行过程中会被划分成很多个子阶段,输入的HTML经历这些子阶段后,最后输入为像素。我们把这样的一个处理流程叫做渲染流水线

按照渲染的时间顺序,流水线可以分为以下几个阶段:构建DOM树、样式计算、布局阶段、绘制、分块、光栅化和合成。

一、构建DOM树

浏览器是无法直接理解和使用HTML的,所以需要将HTML转换为浏览器能够理解的结构——DOM树。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <p>查看DOM树</p>
  </body>
  <script>
    setTimeout(() => {
      var elements = document.getElementsByTagName('p'); // 获取元素集合
      for (var i = 0; i < elements.length; i++) {
        elements[i].style.color = 'red'; // 设置颜色
      }
    });
  </script>
</html>


渲染结果如

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

为了更加直观地理解 DOM 树,你可以打开 Chrome 的“开发者工具”,选择“Console”标签来打开控制台,然后在控制台里面输入“document”后回车,这样你就能看到一个完整的 DOM 树结构,如下图所示:

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

生成DOM树后,DOM节点应该被设计成怎样,也就是说DOM节点的样式还不知道,要让DOM节点拥有正确的样式,就需要用到样式计算了。

二、样式计算

1.CSS转换为浏览器能够理解的结构

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

如上图所示,CSS的来源主要有三种:

  • 通过link引用的外部CSS文件
  • <style>标记内的CSS
  • 元素的style属性内嵌的CSS

和html一样,浏览器也是无法直接理解这些纯文本的CSS样式,所以当渲染引擎接收到CSS文本时,会执行一个转换操作,将CSS文本转换为浏览器可以理解的结构——styleSheets。

想要查看styleSheets,可以在控制台中输入document.styleSheets,然后可以看到如下的结构:

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

2.标准化样式表的属性值

css当中有很多属性值,比如2em、bold,这些类型的属性值不容易被渲染引擎理解,所以需要把值转换成渲染引擎容易理解的、标准化的计算值。这个过程就是属性值的标准化

p {
    font-size:2em;
    font-weight:bold;
}

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

可以看到标准化之后,将red转化成了rgb(255,0,0),2em转换成了64px,bold转换成了700

在看下面的例子,

body { font-size: 20px }
p {color:blue;}
span  {display: none}
div {font-weight: bold;color:red}
div  p {color:green;}

标准化计算后会得到

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

3.计算出DOM树中每个节点的具体样式

样式属性都已经标准化之后,接下来就应该要计算DOM树中每个节点的样式属性了。

这里会涉及到CSS继承,即每个DOM节点除了自身绑定的样式,还有一部分样式会从父节点继承。

3.1 继承自父节点样式

如下图,p节点的属性除了自身绑定的,还会从它的父级继承。

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

3.2样式层叠

CSS样式层叠指的是,当有多个CSS规则应用于同一个HTML元素时,这些规则是如何相互作用并决定元素最终的样式,可以简单理解为样式的叠加在一起的公共作用结果。

CSS层叠遵循特定的规则,以确定哪些属性最终应用于元素。

基本层叠规则如下:

三、布局阶段

DOM树结合DOM树中元素的样式还不能够显示页面,因为我们还需要知道具体DOM应该要放在哪个位置。接下来就需要计算出DOM树中可见元素的几何位置,我们把这个计算过程叫做布局。

Chrome在布局阶段需要完成两个任务:创建布局树和布局计算。

3.1 创建布局树

刚才我们已经得到了DOM树,那么问题来了,DOM树和布局树的区别是什么呢?

DOM树会含有很多不可见的元素,比如head标签,还有使用了display:none的元素。所以布局树需要剔除掉不需要绘画的这部分节点。

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

3.2布局计算

在执行布局操作的时候,会把布局计算的结果重新写回布局树中,所以布局树即使输入内容也是输出内容,这是一个布局阶段一个不合理的地方,因为在布局阶段并没有清晰地将输入内容和输出内容分开。

针对这个问题,Chrome团队正在重构布局代码,下一代布局系统叫LayoutNG,试图更清晰地分离输入和输出,从而让新设计的布局算法更简单。

3.3 分层(图层树)

页面中其实是由很多图层组合而成的,而不是只有一个图层。比如一些复杂的3D变换、页面滚动或者使用z-index做z轴排序等,渲染引擎会为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

通常情况下,并不是布局树的每个节点都会单独对应一个图层,如果一个节点没有对应的层,那么这个节点就从属于节点的图层。如上图中的span标签没有专属图层,那么他们就从属于它们的父节点图层。

提取为单独一层的有两种情况:

  1. 拥有层叠上下文属性的元素会被单独提升为一层。
  2. 需要剪裁的地方也会被创建为图层。

会创建新的层叠上下文的情况如

  1. 元素为 flex布局元素(父元素display:flex | inline-flex ),同时z-index值不是auto
  2. 元素的opacity不是1。
  3. 元素的transform值不是none.
  4. 元素的mix-blend-mode值不是normal
  5. 元素的filter值不是none
  6. 元素的isolation值是isolate
  7. 元素的will-change属性值为上面2~6的任意一个(如will-change:opacity、will-change:transform等)
  8. 元素的-webkit-overflow-scrolling设为touch

剪裁例如需要滚动或者隐藏的情况,单独抽取出来一层会更方便浏览器处理。

通过开发者工具的Layer面板可以看到图层树如下:

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

四、图层绘制

图层树构建完毕之后,渲染引擎会对图层树中的每个图层进行绘制。

渲染引擎会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

可以利用开发者工具的"Layers”标签,选择“document”层,查看某一个图层的绘制列表

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

五、栅格化操作

绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit) 给合成线程。

合成线程会将图层划分为图块(因为HTML文档本身大小可能会超过viewPort),这些图块的大小通常是256 * 256 或者512 * 512。

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。

栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程次。所有的图块栅格化都是在线程池内执行的。

通常,栅格化过程都会用GPU来加速生成,使用GPU生成位图的过程叫快速栅格化或者GPU栅格化,生成的位图被保存在GPU内存中。

渲染流程:HTML、CSS和JavaScript, 是如何变成页面的?

六、合成和显示

一旦所有图块都被栅格化,合成线程就会生成一个绘制图块的命令——DrawQuad,然后将该命令提交给浏览器进程。

浏览器主进程里有一个叫Viz的组件,用来接收合成线程发过来的DrawQuad命令,然后根据DrawQuad命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

到这里,HTML、CSS、JavaScirpt 就组合生成了漂亮的页面了。

总结

渲染过程是一个将HTML转化成DOM,将CSS转化成styleSheets,并将两者结合得到布局树,再划分图层,每个图层有自己的绘制列表,在绘制的过程中会划分图块,通过光栅化生成位图,交由浏览器主进程进行展示。

转载自:https://juejin.cn/post/7393662867155075083
评论
请登录