浏览器从输入URL到页面渲染过程 ——页面渲染流程

之前我有总结过一篇经典面试题:浏览器从输入URL到页面渲染过程 ,接下里我将对某些知识点进行更细致的解析。

浏览器从输入URL到页面渲染过程 系列文章:

(一):浏览器从输入URL到页面渲染过程 —— 浏览器的进程与线程

————————————————————————————————————————————————————

浏览器从输入URL到页面渲染过程 ——页面渲染流程

浏览器的渲染机制很复杂,从输入HTML到页面输出大致分为以下这些步骤:

构建DOM树 > 构建styleSheets树 > 布局 > 分层 > 绘制 >分块 > 光栅化 > 合成
在这里插入图片描述
构建DOM树

  • 为什么要构建 DOM 树?

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

    什么是树结构:节点与节点之间通过父子关系相连接。

    具体流程如下:
    在这里插入图片描述

我们在chrome的控制台输入:document ,便能看到此DOM树结构:
在这里插入图片描述
虽然 DOM树 和 HTML 看起来并没有什么的区别,但是DOM树在内存中是以树状结构存储的,可以被JS代码所查询操作。例如:

document.getElementsByTagName("p")[0].innerText = "black"

在这里插入图片描述
样式计算(构建styleSheets)

  • 为什么要构建 styleSheets ?

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

  • 什么是浏览器可以理解的style?

    rem/em ————> px
    color: blue ————> rgb(0, 0, 255)
    font-weight: blod ————> 700

  • CSS来源有哪些呢?

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

在这里插入图片描述
如果这三个地方都有对同一标签进行样式定义,或者该标签没有被定义样式,那它的最终样式会是什么样的呢?这时候,就需要进行样式计算了。

样式计算规则:继承规则层叠规则

继承规则:当前标签的样式继承了其所有父标签的样式。
层叠规则:多个样式同时作用于该标签时,进行样式层叠。

在这里插入图片描述

当然,我们在控制台输入 document.styleSheets 看到styleSheets结构:
在这里插入图片描述
布局(构建render树)

有了 DOM树 和 styleSheets树,接下来我们就可以将两棵树进行合并,生成render树。

在这里插入图片描述
在生成render树时,浏览器首先遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中,而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.div.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。

当然,这只是第一步,光有这些标签和对应的样式是无法绘出页面图的,渲染进程还需要计算出每一个标签所对应在页面上的物理位置,再将其位置存储到render树中。

分层

虽然有了render树及对应的物理坐标,但浏览器也不能直接进行页面绘制,因为页面上还涉及许多复杂的样式:transform, animation 动画、scroll,z-indexing改变层级等等,浏览器则为这些特殊的节点建立一个对应图层,生成图层树(LayerTree),将这些图层合并在一起,就是一整个页面的样式(类似于 photoshop 的图层)。

那什么时候会被提升为一个专门的图层,哪些应该被包含在同一图层?

1、拥有层叠上下文属性的元素

什么是层叠上下文?其实就是我们熟悉使用的z-index,MDN上是这么定义的
在这里插入图片描述
其中有一句话很重要:

	重要的是,其子级层叠上下文的 z-index 值只在父级中才有意义。

不知你在使用z-index时,有没有遇到这样一个情况:

有时,你想要某一个图片或者文字 置于 某一块 的上方,于是使用z-index进行层级提升,但是就算设置了z-index确没有起作用。

正式因为 子级层叠上下文的 z-index 值只在父级中才有意义 ,只有当 父级的 position 为 absolut 或者 relative 时才有效。

2、需要剪裁的地方也会被创建为图层。

当父容器的宽高不足以撑起子容器的宽高,出现滚动条 或者 设置 父容器 为overflow :hode 等等,子容器页面就会被裁剪,这时浏览器也会为其创建出单独的图层。

如何查看图层?

打开 chrome 控制台,选择 Layers, 点击旋转按钮,对图层进行拖拽到有一定的倾斜度时,便能很直观的看到页面的图层分布 :
在这里插入图片描述
绘制

创建完图层后,接下来就是对图层进行绘制绘制,简单的说就是将图层重叠在一起。

如果给我们一张图,我们会怎样进行绘制呢?
在这里插入图片描述
一般我们会对这张图进行拆分:

1、先画出最外层的红色框图
2、在画出中间层绿色框图
3、最后画出里面层黄色框图

浏览器也是如此,将这些图层拆分为一条条的指令,然后一条一条逐步执行,最后进行绘制:
在这里插入图片描述
分块

为什么需要分块? 再此之前我们需要了解一下什么是视口:我们在当前屏幕区域能看到的模块就叫视口。当页面内容很长时,页面就会出现滚动条,但是当前视口大小有限,我们只能看到一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要。
在这里插入图片描述
这个时候渲染进程会再次将这些图层分成很多图块。
在这里插入图片描述

格珊化

什么是格珊化?

渲染进程将这些图层分成很多图块后,然后按照视口附近的图块来通过 栅格化 优先生成位图。所谓栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位。

最后,我们来看一张流程图:
在这里插入图片描述
之前的生成DOM树、styleSheets树、render 树、分层、绘制都是在渲染引起的主线程中运行的, 绘制列表记录好绘制顺序和绘制指令的列表后,将其提交给渲染引擎中的合成线程,渲染进程还维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的。通常,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。

合成与显示

一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

至此,整个渲染流程就走完了,总结为以下8步:

1、渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构。
2、渲染引擎将 CSS 样式表转化为浏览器可以理解的 styleSheets树,并计算好 DOM 节点的样式。
3、将DOM与styleSheets树进行合成,创建布局(render)树,并计算元素的布局信息。
4、对布局树进行分层,并生成分层树。为每个图层生成绘制列表,并将其提交到合成线程。
5、合成线程将图层分成图块。
6、在光栅化线程池中将图块转换成位图。
7、合成线程发送绘制图块命令 DrawQuad 给浏览器进程。
8、浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

最后抛出两个问题:

1、为什么在优化 Web 性能的方法中,减少重绘、重排是一种很好的优化方式?

结合上文我们了解到,在布局这一过程中,将DOM树与styleSheets 合成render树后,渲染进程还需要计算出每一个标签所对应在页面上的物理位置,再将其位置存储到render树中,在执行下一步。

如果我们对页面进行了重排(改变标签的宽高、显示隐藏等物理层面的几何属性),那么渲染引擎将会把这一整套渲染流程重新跑一遍,浪费时间和空间。

如果我们对页面进行了重绘(改变标签的颜色、类名等样式属性),那么渲染引擎会直接进入 绘制 阶段,相较与重排,也提升了不少性能。

如果我们的操作不涉及重排和重绘,那么渲染引擎会直接进入 合成 阶段,大大的提高了性能。

2、如果下载 CSS/JS 文件阻塞了,会阻塞 DOM 树的合成吗?会阻塞页面的显示吗?

答案是肯定的,不论CSS还是JS文件下载阻塞,都会阻塞后续的流程。当从服务器接收HTML页面的第一批数据时,DOM解析器就开始工作了。
第一种情况:在解析过程中,如果遇到了JS脚本,如下所示:

<html>
    <body>
        哈哈哈
        <script>
        document.write("--限制性js脚本")
        </script>
    </body>
</html>

那么DOM解析器会先执行JavaScript脚本,执行完成之后,再继续往下解析。

第二种情况:我们内联的脚本替换成js外部文件,如下所示:

<html>
    <body>
        哈哈哈
        <script type="text/javascript" src="foo.js"></script>
    </body>
</html>

这种情况下,当解析到JavaScript的时候,会先暂停DOM解析,并下载foo.js文件,下载完成之后执行该段JS文件,然后再继续往下解析DOM。这就是JavaScript文件为什么会阻塞DOM渲染。

第三种情况,还是看下面代码:

<html>
    <head>
        <style type="text/css" src = "theme.css" />
    </head>
    <body>
        <p>哈哈哈</p>
        <script>
            let e = document.getElementsByTagName('p')[0]
            e.style.color = 'blue'
        </script>
    </body>
</html>

当我在JavaScript中访问了某个元素的样式,那么这时候就需要等待这个样式被下载完成才能继续往下执行,所以在这种情况下,CSS也会阻塞DOM的解析。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值