依然是网课学习,做的笔记,比较乱哈

感谢duyi的袁老师

在浏览器地址栏输入网址后,通过网络获取到html文档然后进行渲染工作

浏览器是如何渲染页面的?

当浏览器的网络线程收到html文档后,会产生一个渲染任务,并将该任务传给渲染主线程的消息队列

在时间循环的机制下,渲染主线程将消息队列中的渲染任务取出,开始进行渲染的流程

浏览器的渲染流程分为:HTML解析->样式计算->布局->分层->绘制->分块->光栅化->画


1.解析html-parse html

html文档会被解析为dom树(document object model)cssom(css object model)树

dom树是一个对象,通过console.dir(document)即可查看

cssom树根节点为StyleSheetList

一个页面中有行内样式,内联样式,外联样式,浏览器默认样式每多一个样式,StyleSheetList下就会多一个子节点(CSSStyleSheet),每个子节点下有多个规则对象(CSSStyleRule)

我们可以通过stylesheetlist来操作样式,例如进入百度首页,在控制台输入[document.styleSheets[0].addRule('div','border:1px solid red')]这样该页面的所有div都会有边框样式了

浏览器渲染原理_浏览器

解析过程中遇到css就解析,遇到js就执行,为提高效率开始解析前会启动一个预解析线程,先下载html中的外部css和js文件.

如果主线程解析到<link>位置,此时如果外部css还没有下载解析完毕,主线程不会等待,会继续解析后面的html.这是因为下载和解析css的工作是在预解析线程中进行的.这是css不会阻塞html解析的根本原因.

如果主线程解析到script位置,会停止解析html,等待js文件下载好,并将全局代码解析完成后,才能继续解析html.这是因为js代码的执行过程中可能会修改当前的DOM树,所以DOM树的生成必须暂停.这就是js会阻塞html解析的根本原因.

第一步完成后,会得到dom树和cssom树,浏览器的默认样式,内部样式,外部样式,行内样式都会包含在cssom树中.

浏览器渲染原理_浏览器_02

2.样式计算 Recalculate Style

主线程会遍历到的DOM树,依次为树中的每个节点计算出它的最终样式(计算后的样式),称之为computed style.

最终样式通过js获取,可以使用 getComputedStyle()

比如单纯写一个div,不加任何样式,但是他还是有样式的,浏览器会将默认样式赋予这个div,在浏览器中的开发工具中可以看到div的最终样式,如图

浏览器渲染原理_浏览器_03

在此过程中,很多预设值会变成绝对值,比如red会变成rgb(255,255,0);相对单位会变成绝对单位,比如em变成px.
本阶段完成后,会得到一个带有样式的DOM树.

浏览器渲染原理_浏览器_04

css属性值的计算过程

层叠

继承

视觉格式化模型

盒模型

包含块

3.布局 Layout

布局阶段会依次遍历DOM树的每一个节点,计算节点的几何信息.例如节点的宽高,相对包含块的位置.

大部分时候,DOM树和布局树并非一一对应.

比如display:none的节点没有几何信息,因此不会生成到布局树;又比如使用了伪元素选择器,虽然DOM树中不存在这些伪元素节点,但是他们有用几何信息,所以会生成到DOM树中.还有匿名行盒,匿名块盒等等都会导致DOM树和布局树无法一一对应.


块盒:例如h1-h6,div,p,ul,ol,li,dl,dt,dd,header,main,footer等,像容器一样的就是块盒,块盒独占一行,display是block

行盒:例如a,span,em,strong,img,input,label,textarea,select,button等,行盒不换行,display是inline


内容必须在行盒中,不能放到块盒

行盒 与 块盒不能相邻

浏览器渲染原理_渲染原理_05

浏览器渲染原理_浏览器_06

js虽然不能直接获取布局树中的对象内容,但是可以间接获取一些,例如:

document.body.clientWidth

4.分层 Layer

主线程会使用一套复杂的策略对整个布局树进行分层

分层的好处在于,将来某一层发生变化后,只会对该层进行处理,从而提高效率

滚动条,堆叠上下文,transform,opacity等样式都会多多少少影响分层结果,也可以通过will-change属性更大程度的影响分层结果,例如:

<div class="dan-ceng"></div>
<style>
  .dan-ceng{
  will-change:transform;
  }
</style>

这样,此div就单独为一个层了


浏览器渲染原理_浏览器_07

5.绘制 Paint

主线程会为每个层生成绘制指令集,用于描述这一层的内容应该如何画出来.

指令就是告诉浏览器在什么位置画一个什么东西

完成绘制后,主线程将每个层的绘制信息提交给合成线程,剩余的工作将由合成线程完成.

浏览器渲染原理_渲染原理_08

浏览器渲染原理_渲染原理_09

6.分块 Tiling

合成线程首先对每个图层分块,将其划分为更多的小区域.

他会从线程池中拿取多个线程来完成分块工作.

浏览器渲染原理_浏览器_10

浏览器渲染原理_渲染原理_11


7.光栅化 Raster

分块完成后,进入光栅化阶段.

合成线程会将块信息交给GPU进程,以极高的速度完成光栅化.

GPU进程会开启多个线程来完成光栅化,并且优先处理靠近视口区域的块.

光栅化的结果,就是一块一块的位图.

浏览器渲染原理_渲染原理_12

浏览器渲染原理_浏览器_13

8.画 Draw

最后就是画.

合成线程拿到每个层,每个块的位图后,生成一个个[指引 quad]信息.

指引会标识出每个位图应该画到屏幕的哪个位置,以及会考虑到旋转 缩放等变形.

变形发生在合成线程,与渲染主线程无关,这就是 transform 效率高的本质原因.

合成线程会把 quad 提交给 GPU 进程,由 GPU 进程产生系统调用,提交给 GPU 硬件,完成最终的屏幕成像.

浏览器渲染原理_浏览器_14

完整过程

浏览器渲染原理_浏览器_15

什么是 reflow?

reflow 的本质就是重新计算 layout 树以及8个步骤中的后续流程.

当进行了 会影响布局树 的操作后,需要重新计算布局树,会引发 reflow.

为了避免连续多次的操作导致布局树反复计算,浏览器会合并这些操作,当 JS 代码全部完成后再统一进行计算.

所以,改动属性造成的 reflow 是异步完成的.

正因如此,当 js 获取布局属性时,就可能造成无法获取到最新的布局信息.

浏览器在反复权衡下,最终决定获取属性立即 reflow.


浏览器渲染原理_渲染原理_16


什么是repaint?

repaint 的本质就是重新根据分层信息计算了绘制指令.

当改动了可见样式后,就需要重新计算,会引发 repaint.

由于元素的布局信息也属于可见样式,所以 reflow 一定会引起 repaint.

浏览器渲染原理_浏览器_17

为什么transform效率高

因为 transform 既不会影响布局也不会影响绘制指令,他影响的只是渲染流程的最后[draw]阶段

由于 draw 阶段在合成线程中进行,所以 transform 的变化几乎不影响渲染主线程.反之,渲染主线程无论如何忙碌,也不会影响 transform 的变化.

浏览器渲染原理_渲染原理_18