su37josephxia/frontend-interview

Day28 - 简述一下浏览器渲染过程

su37josephxia opened this issue · 22 comments

浏览器的渲染过程主要分成四大部分:
构建 DOM 树 -> 计算 Render 树 -> 布局 Render 树 -> 渲染 Render 树

下面简述每一步都做了什么

  1. 构建 DOM 树
  • 构建 DOM 树是将浏览器所无法理解的 HTML 文本转换成浏览器能够理解的结构,我们称之为 DOM 树
  1. 计算 Render 树
  • 这一步是将页面中的css进行优先级的计算,然后转换成 styleSheets,并计算每个 DOM 节点的样式
  1. 布局 Render 树
  • 这一步是浏览器计算每个 DOM 节点在屏幕中的位置
  1. 渲染 Render 树
  • 这一步就是应用计算出来的规则,利用显卡将内容绘制到屏幕上
  1. 浏览器首先使用 HTTP 协议或者 HTTPS 协议,向服务端请求页面;

  2. 把请求回来的 HTML 代码经过解析,构建成 DOM 树;

  3. 计算 DOM 树上的 CSS 属性;

  4. 最后根据 CSS 属性对元素逐个进行渲染,得到内存中的位图;

  5. 一个可选的步骤是对位图进行合成,这会极大地增加后续绘制的速度;

  6. 合成之后,再绘制到界面上。

  • DOMTree的构建
    • 从网络或者磁盘下读取的HTML原始字节码,通过设置的charset编码,转换成相字符
    • 通过词法分析器,将字符串解析成Token,Token中会标注出当前的Token是开始标签,还是结束标签,或者文本标签等
    • 浏览器会根据Tokens里记录的开始标签,结束标签,将Tokens之间相互串联起来,生成Nodes并构建DOM树
  • CSSTree的构建
    • DOM会记录页面的内容,但是浏览器还需要知道这些内容该用什么样式去展示,所以还需要构建CSSOMTree。CSSOM的生成过程和DOM的生成过程十分相似,也是:1.解析,2.Token化,3.生成Nodes并构建CSSOMTree
    • CSSOMTree需要等到完全构建后才可以被使用,因为后面的属性可能会覆盖掉前面的设置
  • 渲染树的构建
    • 已经拥有了完整的DOM树和CSSOM树,但是DOM/CSSOM树本身并不能直接用于排版和渲染,浏览器还会生成另外一棵树:Render树
    • Render 树上的每一个节点被称为:RenderObject
    • RenderObject跟 DOM 节点几乎是一一对应的,当一个可见的 DOM 节点被添加到 DOM 树上时,内核就会为它生成对应的 RenderOject 添加到 Render 树上。
    • Render 树是衔接浏览器排版引擎和渲染引擎之间的桥梁,它是排版引擎的输出,渲染引擎的输入
    • 浏览器渲染引擎并不是直接使用Render树进行绘制,为了方便处理Positioning,Clipping,Overflow-scroll,CSS Transfrom/Opacrity/Animation/Filter,Mask or Reflection,Z-indexing等属性,浏览器需要生成另外一棵树:Layer树
  • 布局
    • 到目前为止,浏览器计算出了哪些节点是可见的以及它的信息和样式,接下来就需要计算这些节点在设备视口内的确切位置和大小,这个过程我们称之为“布局
  • 渲染
    • 将渲染树中的每个节点转换成屏幕上的实际像素:浏览器通过发出“Paint Setup”和“Paint”事件,将渲染树转换成屏幕上的像素,至此浏览器渲染完成

浏览器渲染过程

  • 渲染进程把HTML转变为 DOM 树形结构
    • 浏览器中的 HTML 解析器可以把 HTML 字符串转换成 DOM 结构
    • HTML 解析器边接收网格数据边解析 HTML
    • 解析 DOM
      • HTML 字符串转 Token
      • Token 栈用来维护节点之间的父子关系,Token 会依次压入栈中
      • 如果是开始标签,把 Token 压入栈中并且创建新的 DOM 节点并添加到父节点的 children 中
      • 如果是文本 Token,则把开始节点添加到栈顶元素的 children 中,文本 Token 不入栈
      • 如果是结束标签,则开始标签出栈
  • 渲染进程把 CSS 文本转为浏览器中的 stylesheet
  • 通过 stylesheet 计算 DOM 节点的样式
  • 根据 DOM 树创建布局树
    image
  • 并计算各个元素的布局信息
  • 根据布局树生成分层树
  • 根据分层树进行生成绘制步骤
  • 把绘制步骤交给渲染进程中的合成进程合成
  • 合成线程将图层分为图块(title)
  • 合成线程会把分好的图块发给栅格化线程池,栅格化线程池会把图块(title)转化为位图
  • 而其实栅格化线程在工作的时候会把栅格化的工作交给 GPU 进程来完成,最终生成的位图就保存在 GPU 内存中
  • 当所有的图块都光栅化之后,合成线程会发送绘制图块的命令给浏览器主进程
  • 浏览器主进程然后会从 GPU 内存中取出位图显示在页面上

浏览器渲染过程

  1. 浏览器获取HTML⽂件,然后对⽂件进⾏解析,形成DOM Tree
  2. 与此同时,进⾏CSS解析,⽣成Style Rules
  3. 接着将DOM Tree与Style Rules合成为 Render Tree
  4. 接着进⼊布局(Layout)阶段,也就是为每个节点分配⼀个应出现在屏幕上的确切坐标
  5. 随后调⽤GPU进⾏绘制(Paint),遍历Render Tree的节点,并将元素呈现出来
    浏览器如何解析css选择器?
    浏览器会『从右往左』解析CSS选择器。 我们知道DOM Tree与Style Rules合成为 Render Tree,实际上是需要将Style Rules附着到DOM Tree上,因此需要根 据选择器提供的信息对DOM Tree进⾏遍历,才能将样式附着到对应的DOM元素上。 以下这段css为例 .mod-nav h3 span {font-size: 16px;}
    若从左向右的匹配,过程是: 1. 从 .mod-nav 开始,遍历⼦节点 header 和⼦节点 div 2. 然后各⾃向⼦节点遍历。在右侧 div 的分⽀中 3. 最后遍历到叶⼦节点 a ,发现不符合规则,需要回溯到 ul 节点,再遍历下⼀个 li-a,⼀颗DOM树的节点动不动上 千,这种效率很低。 如果从右⾄左的匹配: 1. 先找到所有的最右节点 span,对于每⼀个 span,向上寻找节点 h3 2. 由 h3再向上寻找 class=mod-nav 的节点 3. 最后找到根元素 html 则结束这个分⽀的遍历。 后者匹配性能更好,是因为从右向左的匹配在第⼀步就筛选掉了⼤量的不符合条件的最右节点(叶⼦节点);⽽从左向 右的匹配规则的性能都浪费在了失败的查找上⾯。

首先从上到下解析html构建dom树,首先预解析会优先把所有src属性都发出请求不会被停止,html解析器如果遇到普通的script标签就停下来先执行script的内容,遇到css就用css引擎解析。最后生成一个dom树和css树,然后合成渲染树。进行重排再进行重绘,将页面展示出来。

构建DOM树
        创建布局树分层图层绘制光栅化合成显示
样式计算 ↗


  1. 构建DOM树- 浏览器不理解html DOM结构, 所以渲染进程将其转为能读的DOM树结构
  2. 样式计算 - 同样, css样式文件浏览器也不能理解,所以也要将其转为styleSheets样式
    • 这步主要是将象内联样式,link等样式转换为样式表,再将象red/rem转为rgb和px, 在计算每个节点的具体样式(继承和层叠)
  3. 创建布局树 - 接下来创建布局树, 根据页面可见的DOM添加到布局树中
  4. 分层 - 我们用ps时候一个图片其实是分很多层合成的,渲染也是,接着渲染进程将布局树中特殊的DOM单独抽层,方便实现复杂效果或交互
  5. 图层绘制 - 然后进行图层绘制,渲染引擎会把一个图层的绘制拆分成很多小的绘制指令, 再把这些指令按照顺序组成一个待绘制列表, 根据列表绘制出图层
  6. 光栅化 - 因为一个页面可能很大,存在不可见的范围,所以图层绘制完后,会进行分块,靠近可视范围的,会优先生成位图,而将图块转为位图就叫做光栅化
  7. 合成显示 - 合成线程发送绘制图块命令DrawQuad给浏览器进程, 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上

浏览器渲染过程简述

  • 浏览器进程中网络线程获取到html文件
  • 通过 IPC 将数据传递给渲染器进程
  • 主线程将html解析构造DOM树
  • 解析css生成样式树
  • 根据DOM树和样式树生成 Layout Tree
  • 通过遍历 Layout Tree 生成绘制顺序表
  • 遍历 Layout Tree 生成 Layer Tree
  • 主线程将Layer Tree和绘制顺序信息传递给合成器线程
  • 合成器线程按规则进行分图层,并把图层分为更小的图块 (tiles) 传递给 栅格线程进行栅格化
  • 栅格化完成后合成器线程将这些信息合成合成器帧
  • 合成器帧通过 IPC 传回给浏览器进程
  • 浏览器进程再将信息传递到 GPU 进行渲染

浏览器获取HTML字节数据后会将字节解析为DOM树,当遇到标签时,浏览器就开始解析CSS,像构建DOM树一样构建CSSOM树,然后将DOM树、CSSOM树结合在一起,构建渲染树,最后是计算每个节点的布局,开始绘制渲染页面

  1. 构建对象模型(DOM,CSSOM)
    浏览器获取到html页面后,先遍历文档节点,生成dom树;
    DOM解析的同时可以解析css,生成CSSOM\
  2. 构建渲染树(Render tree)
    通过DOM树和CSS规则树,构建渲染树。
    浏览器会先从DOM树的根节点开始遍历每个可见节点,然后对每个可见节点找到适配的CSS样式规则并应用\
  3. 布局
    渲染树生成后,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系,从而去计算出每个节点在屏幕中的位置。\
  4. 绘制
    根据计算好的信息绘制整个页面

渲染流程有四个主要步骤:

1.解析HTML生成DOM树 - 渲染引擎首先解析HTML文档,生成DOM树
2.构建Render树 - 接下来不管是内联式,外联式还是嵌入式引入的CSS样式会被解析生成CSSOM树,根据DOM树与CSSOM树生成另外一棵用于渲染的树-渲染树(Render tree),
3.布局Render树 - 然后对渲染树的每个节点进行布局处理,确定其在屏幕上的显示位置
4.绘制Render树 - 最后遍历渲染树并用UI后端层将每一个节点绘制出来
以上步骤是一个渐进的过程,为了提高用户体验,渲染引擎试图尽可能快的把结果显示给最终用户。它不会等到所有HTML都被解析完才创建并布局渲染树。它会在从网络层获取文档内容的同时把已经接收到的局部内容先展示出来。

  • 浏览器的渲染过程:DOMTree的构建-->CSSOMTree的构建-->渲染树的构建-->布局-->渲染
  • 在构建DOMTree的时候,按照自上而下,但遇到script标签没有设置async/defer属性时,这个加载过程是下载并执行完全部的代码,此时,DOM树还没有完全创建完毕,这个时候如果js企图访问script标签后面的DOM元素,浏览器就会抛出找不到该DOM元素的错误。
  • DOMTree创建完成后,浏览器开始解析css并创建CSSOM树,CSSOM树上每个节点对应着网页里每个元素的样式,并且此时浏览器也可以通过 JavaScript 操作DOM/CSSOM树,动态改变它的结构。
  • DOM/CSSOM树本身并不能直接用于排版和渲染,浏览器还会生成另外一棵树:Render树。
  • 到目前为止,浏览器计算出了哪些节点是可见的以及它的信息和样式,接下来就需要计算这些节点在设备视口内的确切位置和大小,这个过程我们称之为“布局”。布局最后的输出是一个“盒模型”:将所有相对测量值都转换成屏幕上的绝对像素。
  • 最后,将这些信息传递给最后一个阶段:将渲染树中的每个节点转换成屏幕上的实际像素:浏览器通过发出“Paint Setup”和“Paint”事件,将渲染树转换成屏幕上的像素。
  1. 解析HTML,构建DOM树

  2. 解析CSS,生成CSS规则树

  3. 合并DOM树和CSS规则,生成render树

  4. 布局render树(Layout/reflow),负责各元素尺寸、位置的计算

  5. 绘制render树(paint),绘制页面像素信息

  6. 浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上

  1. 浏览器,获取输入的url
  2. DNS层,进行域名解析,解析成IP地址
  3. TCP层,进行TCP连接(三次握手和四次挥手)
  4. HTTP层,进行HTTP请求
  5. 服务器,处理请求
  6. 回到浏览器渲染界面
    • parse:html解析和css解析
    • attachment :合并树,从dom根结点开始遍历每一个可见的节点,为其找到适配的css规则并应用。将两个树合并成render tree
    • layout:计算元素的布局信息(元素相对于设备视窗内的确切位置和大小)
    • painting:遍历渲染树,进行每个节点的绘制
  1. 将HTML转化成DOM树。(document)
  2. 对CSS进行解析,解析成styleSheets。CSSOM(document.styleSeets)
  3. 计算出DOM树中每个节点的具体样式(Attachment)
  4. 创建渲染(布局)树,将DOM树中可见节点,添加到布局树中。并计算节点渲染到页面的坐标位置。(layout)
  5. 通过布局树,进行分层 (根据定位属性、透明属性、transform属性、clip属性等)生产图层树
  6. 将不同图层进行绘制,转交给合成线程处理。最终生产页面,并显示到浏览器上 (Painting,Display)

1分别解析HTML和css, 生成浏览器识别的dom树和css规则树
2将dom和css规则合并成渲染树
3将渲染树进行布局,计算位置和大小
4渲染到页面上

1.生成dom树
2.将dom树与样式结合cssom
3.进行计算并绘制
4.显示到屏幕上

● 浏览器将获取的HTML文档解析成DOM树。
● 解析CSS,构成层叠样式表模型CSSOM(CSS Object Model),CSS解析可以与DOM解析同时进行。。
● 将DOM和CSSOM(层叠样式表模型)合并为渲染树(rendering tree),代表一系列将被渲染的对象。
● 渲染树的每个元素包含的内容都是计算过的,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有的元素
● 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting。
● 在此过程中,js的解析会阻塞DOM和CSSOM的构建,并且JavaScript和CSS的某些操作往往会多次修改DOM或者CSSOM

将请求到 html 和 css 文件解析为 dom 树 和 层叠样式表 -> 合并得到渲染树 -> 进行布局 -> 最后将渲染树绘制到屏幕上

我们谈论渲染过程,那么就从浏览器从服务端拿到页面数据开始说起

  1. 第一步是处理HTML标记并构造DOM树
  2. 第二步是处理CSS并构建CSSOM树
  3. 第三步是将DOM和CSSOM组合成一个Render树,计算样式树或渲染树从DOM树的根开始构建,遍历每个可见节点。
  4. 第四步是在渲染树上运行布局以计算每个节点的几何体
    第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流。
  5. 最后一步是将各个节点绘制到屏幕上

首先浏览器从网络或硬盘中获得HTML字节数据开始:

  1. 开始将HTML解析,构建DOM树
  2. CSS解析,构建CSSOM树(浏览器遇到link标签时就开始解析CSS)
  3. 合并DOM树和CSSOM树,构建渲染树
  4. 渲染树构建好之后,浏览器得到了每个节点的内容和样式,然后开始计算每个节点在浏览器中的位置和大小,进行layout布局
  5. 布局完成,浏览器立即发出Paint事件,将渲染树绘制成像素,绘制完成后在屏幕中呈现出来

浏览器的渲染过程大致可以分成几个关键的阶段:

首先就是浏览器会利用HTML文档字符串去生成 DOM 树和 CSS 树,随后,通过这两棵树去生成一棵 render 树,然后用这颗 render 树去确定界面上每一个盒子的尺寸大小以及它们的外观,然后确定出来的这些尺寸大小、外观信息就交给 GPU 做界面上的最终显示,这就是一个大致的过程。