chrome浏览器渲染流程

542 阅读5分钟

浏览器是什么时候开始渲染的?

渲染时间点.png

  1. 浏览器通过网络请求得到html文件
  2. 生成一个渲染任务,传递到消息队列中
  3. 渲染主线程通过事件循环拿到渲染任务开始渲染

渲染流程

渲染流程图.png

  • HTML 解析
  • 样式计算
  • 布局
  • 分层
  • 绘制
  • 分块
  • 光栅化

每一个流程都有输入和输出,上一个流程的输出就是下一个流程的输入

第一步 解析HTML

  1. 解析HTML遇到css解析css,遇到js解析js,但是浏览器会开一个预解析线程负责下载和解析css,js,率先下载外部的css和js
  2. 如果主线程遇到link标签会继续解析html,当css下载解释完成再交给主线程,是不会暂停等待的,所以css是不会阻塞html解析
  3. 如果主线程遇到script标签,会暂停等待html下载和执行同步代码完成才会继续解析html,因为js中可能会修改了dom元素,必须等待js执行完成,这就是为什么js会阻塞html解析的原因
  4. 解析html首先会创建一个document对象,我们所有的DOM对象都是添加到document对象中
  5. 把我们在html写的标签变成对象格式,因为对象可以方便我们在js中操作DOM
  6. 解析css也会创建一个StyleSheetList对象, 对象中有选择器名称和我们添加的css属性
  7. 最终把 html 和 css 解析成两个对象

当这一步完成就可以得到DOM树和CSSOM树

第二步 样式计算

我们经常会使用一些不确定的单位,这一步就是把这些预设值变成绝对值, 比如

  • em (相对父元素)
  • rem (相对根元素)
  • 百分比
  • . . . . . .
  1. 主线程遍历得到DOM树, 依次为每个节点计算出样式信息, 这一步叫做 Computer Style
  2. 把我们的预设值变成绝对值, 比如 background-color: white, 变成 background-color: rgb(255,255,255), 相对单位变成绝对单位, em 变成 px

这一步我们可以得到带有样式的DOM树

第三步 布局

  1. 依次遍历,通过每个dom的节点的几何信息,比如宽高,相对包含块位置,计算出元素所在的位置
  2. 但是往往DOM树和布局树并不是一一对应的

布局树.png

  • 当元素使用了 display: none 属性时, 在dom树中是有节点的,但是他并没有几何信息,所以没有必要添加到布局树当中
  • 当使用了伪元素时,在dom树中是没有伪元素的节点信息的,但是有几何信息,是需要渲染到页面上的,所以要添加到布局树当中

完成这一步可以得到布局树

第四步 分层

浏览器会有一套复杂的策略对页面进行分层,分层可以提高浏览器的渲染效率,不会因为一个地方的变动而刷新整个页面,只是重新渲染一部分

  • 我们在写css属性的时候有可能会影响到浏览器的分层结果,比如 z-index、滚动条、transform、opacity,也可以使用will-change更大程度影响分层结果

分层.png

  • 可以通过f12控制台中的Layers面板查看你的网页分层

第五步 生成绘制指令

  1. 这里会为每一个图层生成出如何绘制的指令

到这一步渲染主线程的工作就完成了,剩下交给合成线程完成

第六步 分块

  1. 分块会将每一个图层切分成更小的区域,分块的工作是交给多线程完成的,他会通过线程池调取线程完成分块

第七步 光栅化

  1. 光栅化就是把每一小块区域变成位图,位图就是由多个像素点组成,这里已经生成了颜色信息了
  2. 并且光栅化会优先处理靠近用户窗口的区域

第八步 画

  1. 合成线程拿到每一个图层,和位图之后,生成quad信息
  2. quad信息会指示出内容应该在什么位置,还有考虑是否有缩放,变形
  3. transform就是在合成线程中完成的,与主线程无关,这就是transform效率高的原因
  4. 最后把quad信息交给GPU就可以把页面绘制出来了

什么是 reflow (重排)

  • reflow的本质就是布局被修改了,需要重新计算布局树
  • 浏览器为了提升效率,把多次修改导致布局树重复计算的操作,合并成一次操作
  • 这也导致了我们有时候获取dom可能得到的不是想要的数据
  • 浏览器也是有解决办法的,当 JS 获取布局属性时立即触发reflow那么就可以的到我们想要的数据了
// 举一个我遇到过的例子 在我做轮播图的时候,显示到最后一张图片时下一张应该立即跳到第一张
// 这个时候就要把轮播图滚动的过渡取消掉,但是后续还是需要这个过渡的
// 先把过渡取消
大盒子.style.transition = 'none'

// 这里我们改变轮播图的位置
图片.style.translate = '0px'
// 读取布局属性导致 reflow
图片.clientWidth

// 再添加过渡
大盒子.style.transition = 'all .5s'

这个例子的意思就是你每一次执行的js代码对布局树的多次更改只会执行最终更改的结果,这个案例取消transition后面又添加了transition属性,这两部被合并了所以最终取消过渡失败,如果需要把两步分开在中间加一个读取布局属性就可以了

什么是 repaint (重绘)

  • repaint的本质就是根据分层信息重新生成绘制指令
  • 当我们修改了可见样式,比如 background color属性都会引起 repaint
  • 由于元素的布局信息也属于可见样式,所以 reflow 一定会引起 repaint。