HTML、CSS、JavaScript是怎么变成页面的

我们编写好 HTML、CSS、JavaScript 等文件,经过浏览器就会显示出漂亮的页面,但是你知道它们是如何转化成页面的吗?这背后的原理你知道多少呢?我们都知道 HTML 的内容是由标签和文本组成(如:<p>hello world</p>p 是标签,“hello world” 是文本)。每个标签都有它自己的语意,浏览器会根据标签的语意来正确展示 HTML 内容。而 CSS 则是让页面变得好看的关键;而 JavaScript 则是我们和网页进行交互的核心。由于渲染机制过于复杂,所以渲染模块在执行过程中会被划分为很多子阶段,输入的 HTML 经过这些子阶段,最后输出像素。我们把这样的一个处理流程叫做渲染流水线,其大致流程如下图所示:
按照渲染的时间顺序,流水线可分为如下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、图层绘制、光栅化和合成并显示。接下来我们就从这些阶段去探索一下吧。

构建 DOM 树

为什么要构建 DOM 树呢?因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。
相信大家对树形结构也不陌生了,DOM 树是根据HTML页面标签的层级关系来生成的,看了下面的图,应该也就对 DOM 树会有更清晰的认识。
在这里插入图片描述

样式计算

有了DOM 树,但是页面的标签结构,但是页面是怎么和我们写的 CSS 代码产生关联的呢?和我们写的 CSS 样式有三种形式,一种是通过 <link> 标签来引入的;第二种是写在 <style><style> 标签中的;最后一种就是行内样式了。和 HTML 文件一样,浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS文本转换为浏览器可以理解的结构——styleSheets。我们可以在页面中输入 document.styleSheets 来查看浏览器解析后的CSS 结构。
在这里插入图片描述
在这个过程中,浏览器会对 CSS样式进行转换,使其标准化。比如转换成类似下面的这种代码。除此之外,还会对页面上的一些单位,如 em, blue 转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。

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

转换后浏览器需要计算出 DOM 树中每个节点的具体样式。而这些样式会遵循 CSS的继承规则和层叠规则。

布局阶段

现在,我们有 DOM 树DOM 树中元素的样式,但这还不足以显示页面,因为我们还不知道 DOM 元素的几何位置信息。那么接下来就需要计算出 DOM 树中可见元素的几何位置,我们把这个计算过程叫做布局。
Chrome 在布局阶段需要完成两个任务:创建布局树布局计算

  1. 创建布局树
    你也许已经发现了在 HTML 中有很多标签是不可见的,也有一些样式原因,导致页面上的一些元素也是看不见的,那浏览器就需要在把页面显示出来之前,计算哪些是可见的,哪些是不可见的。为了构建布局树,浏览器大致会遍历 DOM 树中的所有可见节点,并把这些节点加到布局中

  2. 布局计算
    经过上面的处理以后,我们就得到了一颗完整的布局树了。接下来,就要计算布局树节点的坐标位置了。在执行布局操作的时候,会把布局运算的结果重新写回布局树中。

    如果下载 CSS 文件阻塞了, 是不会阻塞dom树的构建,因为在 HTML 转化为 DOM树 的过程中,发现文件请求会交给网络进程去请求对应文件,渲染进程继续解析Html。但是会阻塞页面的显示,当计算样式的时候需要等待css文件的资源进行层叠样式。资源阻塞了,会进行等待,直到网络超时,network直接报出相应错误,渲染进程继续层叠样式计算。

分层

现在我们已经得到了一棵完整的布局树,也计算出它相应的位置,我们接下来是不是就要开始绘制了呢?其实不然。相信写过CSS 的小伙伴都用过这么一个属性 – z-index。因为页面中有很多复杂的效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树。如果你熟悉 PS,相信你会很容易理解图层的概念,正是这些图层叠加在一起构成了最终的页面图像。
通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。

图层绘制

在完成图层树的构建之后,渲染引擎会对图层树中的每个图层进行绘制。渲染引擎实现图层的绘制会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在图层绘制阶段,输出的内容就是这些待绘制列表。

光栅化

绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。你可以结合下图来看下渲染主线程和合成线程之间的关系:
在这里插入图片描述
如上图所示,当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程,那么接下来合成线程是怎么工作的呢?
在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要。基于这个原因,合成线程会将图层划分为图块(tile),这些图块的大小通常是 256x256 或者 512x512。然后合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。

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

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

合成和显示

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

总结

到这里,经过这一系列的阶段,编写好的 HTML、CSS、JavaScript 等文件,经过浏览器就会显示出漂亮的页面了。

  • 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。
  • 渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式。
  • 创建布局树,并计算元素的布局信息。
  • 对布局树进行分层,并生成分层树。
  • 为每个图层生成绘制列表,并将其提交到合成线程。
  • 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  • 合成线程发送绘制图块命令DrawQuad给浏览器进程。
  • 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值