您现在的位置是:首页 > 正文

浏览器工作原理--渲染流程:HTML,CSS和JavaScript是如何变成页面的?

2024-02-29 17:29:37阅读 4

一、渲染流水线

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

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

二、构建DOM树

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

(2)DOM树的构建过程

 构建 DOM 树的输入内容是一个非常简单的 HTML 文件,然后经由 HTML 解析器解析,最终输出树状结构的 DOM。

(3)DOM 和 HTML 内容几乎是一样的,但是和 HTML 不同的是,DOM 是保存在内存中树状结构,可以通过 JavaScript 来查询或修改其内容。

三、样式计算(Recalculate Style)

目的:计算出 DOM 节点中每个元素的具体样式,在计算过程中需要遵守 CSS 的继承和层叠两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。

  • 把 CSS 转换为浏览器能够理解的结构

(1)CSS来源

 (2)和 HTML 文件一样,浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets(该结构同时具备了查询和修改功能,这会为后面的样式操作提供基础

  •  转换样式表中的属性值,使其标准化

CSS 文本中有很多属性值,如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。 

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

(1)CSS 的继承规则

CSS 继承就是每个 DOM 节点都包含有父节点的样式。

(2)UserAgent 样式

它是浏览器提供的一组默认样式,如果你不提供任何样式,默认使用的就是 UserAgent 样式。

(3)层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在 CSS 处于核心地位,CSS 的全称“层叠样式表”正是强调了这一点。

四、 布局阶段

(1)计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。

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

  • 创建布局树

 DOM 树还含有很多不可见的元素,比如 head 标签,还有使用了 display:none 属性的元素。所以在显示之前,我们还要额外地构建一棵只包含可见元素布局树。

 步骤:

1、遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;

2、而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树

  •  布局计算

 (1)现在我们有了一棵完整的布局树。那么接下来,就要计算布局树节点的坐标位置了。布局的计算过程非常复杂,我们这里先跳过。

(2)在执行布局操作的时候,会把布局运算的结果重新写回布局树中,所以布局树既是输入内容也是输出内容,这是布局阶段一个不合理的地方,因为在布局阶段并没有清晰地将输入内容和输出内容区分开来。针对这个问题,Chrome 团队正在重构布局代码,下一代布局系统叫 LayoutNG,试图更清晰地分离输入和输出,从而让新设计的布局算法更加简单

五、分层

(1) 渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree).

(2)浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。

(3)布局树节点和图层的关系

 1、并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层

2、可以被提升为单独的一个图层的元素

(1)拥有层叠上下文属性的元素会被提升为单独的一层

明确定位属性的元素、定义透明属性的元素、使用 CSS 滤镜的元素等,都拥有层叠上下文属性。

(2)需要剪裁(clip)的地方也会被创建为图层

出现这种裁剪情况的时候,渲染引擎会为文字部分单独创建一个层,如果出现滚动条,滚动条也会被提升为单独的层

六、 图层绘制

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

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

 从图中可以看出,绘制列表中的指令其实非常简单,就是让其执行一个简单的绘制操作,比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在图层绘制阶段,输出的内容就是这些待绘制列表

 七、栅格化(raster)操作

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

(1)渲染主线程和合成线程之间的关系

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

 (2)视口

通常一个页面可能很大,但是用户只能看到其中的一部分,我们把用户可以看到的这个部分叫做视口(viewport)

  • 合成线程

(1)会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512。

(2)合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位

(3)渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的。

(4)合成线程提交图块给栅格化线程池

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

  2、GPU 操作是运行在 GPU 进程中,如果栅格化操作使用了 GPU,那么最终生成位图的操作是在 GPU 中完成的,这就涉及到了跨进程操作

 从图中可以看出,渲染进程把生成图块的指令发送给 GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中。

八、合成和显示

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

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

九、渲染流水线总结

 (1)整个渲染流程

HTML 到 DOM、样式计算、布局、图层、绘制、光栅化、合成和显示。

(2)完整的渲染流水线示意图:

 (3)大致过程

1、渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构

2、渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets,计算出 DOM 节点的样式。

3、创建布局树,并计算元素的布局信息。

4、对布局树进行分层,并生成分层树

5、为每个图层生成绘制列表,并将其提交到合成线程。

6、合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图

7、合成线程发送绘制图块命令 DrawQuad 给浏览器进程。

8、浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。  

十、重排

 (1)原因:更新了元素的几何属性

1、 从上图可以看出,如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排

2、重排需要更新完整的渲染流水线,所以开销也是最大的

(2)触发时机

  • 添加或删除可见的DOM元素
  • 元素位置改变
  • 元素本身的尺寸发生改变
  • 内容改变
  • 页面渲染器初始化
  • 浏览器窗口大小发生改变

十一、重绘

 原因:更新元素的绘制属性:

 1、从图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘

2、 相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

十二、直接合成阶段

 如果你更改一个既不要布局也不要绘制的属性(比如CSS3的实现的一些动画效果),渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成:

 在上图中,我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率

十三、如何能减少重绘、重排 

1、减少直接操作dom元素,改用className用于控制
2、尽量减少table使用,table属性变化使用会直接导致布局重排或者重绘
3、当dom元素position属性为fixed或者absolute, 可以通过css形变(transform)触发动画效果,此时是不会出发reflow的
4、不要把 DOM 结点的属性值放在一个循环里当成循环里的变量
5、批量更改DOM:如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document

网站文章

  • mac安装mongodb

    mac安装mongodb

    1.下载地址:https://www.mongodb.com/try/download/community2.下载成功后,将安装包解压到当前目录3.使用如下命令将解压后的安装包移动到usr/local...

    2024-02-29 17:29:31
  • odoo的字段属性

    1 store=True,计算字段要加2 readonly=True,设置字段只读3 index=True,增加数据库索引,提高搜索查找速度4 required=True设置字段必填5 size=64,char类型字段设置字符大小?6 default=False,默认值7 oldname=" "8 related=‘ ’ , 关系字段9 compute ...

    2024-02-29 17:29:23
  • asus pc server + geforce titan xp + centos 7

    asus pc server + geforce titan xp + centos 7

    系统安装直接采用光盘安装centos7. (安装的时候由于ssd之前已经有了esxi系统,但是直接安装并不会清除掉里面的东西,所以alt+f3切换到命令行,fdisk p d 1-9进行删除,或者直接 dd if=/dev/zero of=/dev/sdc bs=512 count=10, 相当于破坏了分区表,里面的东西自然也就没了)装完了系统,用户问题。 centos 7 不会容忍你只有一个r

    2024-02-29 17:28:56
  • clickhouse 高可用负载均衡chproxy1.17生产环境配置与使用

    的 HTTP 代理和负载平衡器 主要有这几个功能,详细的可以看官网本文提供一些生产环境的例子。官网的例子都比较简单, 也没有相关配置的解释,本文不涉及https部分ClickHouseappserverstats-raw。

    2024-02-29 17:28:48
  • 数据集【NO.7】无人机航拍数据集——VisDrone2019数据集

    数据集【NO.7】无人机航拍数据集——VisDrone2019数据集

    数据集对应应用场景,不同的应用场景有不同的检测难点以及对应改进方法,本系列整理汇总领域内的数据集。

    2024-02-29 17:28:42
  • vue + elementui 通过父子组件实现弹框

    vue + elementui 通过父子组件实现弹框

    文章目录子组件父组件子组件<template> <ModalComponent name="import-set" title="设置" size="85%" width="...

    2024-02-29 17:28:13
  • java:实现小顶堆(附完整源码)

    java:实现小顶堆(附完整源码)

    2024-02-29 17:28:06
  • 【ZJOI2013】bzoj3112 防守战线

    线性规划+费用流

    2024-02-29 17:27:57
  • 「Torrent文件的奥秘揭示:从字节到资源的计算机网络深度解析!」

    通过深入了解Torrent文件的原理和工作方式,我们能更好地利用它来获取需要的资源,并且注意在合法、安全的前提下进行使用。当用户开始下载一个Torrent文件时,Torrent客户端首先会连接Trac...

    2024-02-29 17:27:29
  • 【C++】:静态转换(static_cast)与动态类型转换(dynamic_cast)

    【C++】:静态转换(static_cast)与动态类型转换(dynamic_cast)

    这篇笔记的内容是:静态类型转换与动态类型转换 在之前我们学过,变量的数据类型可以强制转换为其他数据类型。但由于这种C风格的类型转换可能会出现一些问题,即过于松散的情况,因此C++提出了更加规范、严格的...

    2024-02-29 17:27:22