浏览器渲染原理

目录

  • 总则
  • 解析HTML/Parse HTML
  • 样式计算/Recalculate Style
  • 布局/Layout
  • 分层/Layer
  • 绘制/Paint
  • 分块/Tiling
  • 光栅化/Raster
  • 画/Draw
  • 关于reflow
  • 关于repaint
  • 关于transform

总则

浏览器在进行通信任务时会请求一个HTML文档,并往消息队列里放入一个渲染任务,在事件循环机制下由渲染主线程取出执行
关于事件循环
渲染总共分为以下几个步骤:

解析HTML/Parse HTML
样式计算/Recalculate Style
布局/Layout
分块/Layer
绘制/Paint
分块/Tiling
光栅化/Raster
画/Draw

解析HTML/Parse HTML

  1. 浏览器在获取到HTML之后会将字符串转换为对象并生成DOM树CSSOM树
  2. 渲染主线程在解析HTML中如果遇到了CSS会将其交给预解析线程处理
    预解析线程快速浏览之后如果需要额外下载CSS的话会让网络线程发送请求
    获取CSS之后预解析线程会进行解析并返回一个CSSOM
  3. 在此之后渲染主线程会继续解析HTML并得到DOM
  4. 渲染主线程在遇到JS时会停止一切任务,先交由预解析线程处理
    如果需要下载JS则预解析线程会让网络线程发送请求
    等预解析线程解析完成由渲染主线程执行JS代码
  5. 执行完之后渲染主线程再继续解析HTML并得到DOM

样式计算/Recalculate Style

因为此时CSSOM树中的样式还不是最终的样式
所以渲染主线程还需对其进行计算
具体的计算流程可以参见我的这篇文章
(未动笔,未来可寄)
算出最终的样式后会放入DOM树其对应的节点中

布局/Layout

根据得到的DOM树生成Layout树
Layout树是布局树,每一个节点都有着其几何信息,如宽高,相对于包含块的位置等等
关于包含块可以参见我这篇文章
(未动笔,未来可寄)
Layout树并不跟DOM树一一对应,有些DOM元素的display为none就不会出现在Layout树上,因为其没有对应的布局信息

分层/Layer

浏览器在布局之后会对整个页面进行分层,浏览器会依照自己的策略将整个网页分成若干层
一般浏览器会将可能会被重复修改的元素单独分一层,这样当浏览器发现这个区域需要重新渲染时就只会重新渲染这一层而不会渲染整个页面
每个浏览器的分层策略都是不同的,想要提高单独分层的概率可以再CSS代码中加上一个属性

will-change: transform;

它代表了这个元素在未来可能会产生变化的属性,提供给浏览器参考,提升它被单独分层的概率

绘制/Paint

绘制则是会为每一层生成绘制指令集,用于描述这一层如何画出来

分块/Tiling

到了这一步渲染主线程的任务就完成了,之后的任务它会交给合成线程
合成线程会对每一层进行分块,把每一层分成更小的区域
这么做的好处就是当浏览器渲染不及时时,浏览器便可以优先渲染视口所在的块,保证用户体验
合成线程会从线程池中取出更多线程来一起完成任务

光栅化/Raster

接下来就是由GPU完成
GPU会将每块都变成位图
GPU会优先处理视口的块
同样GPU也会从线程池中取出更多线程来完成任务
最后GPU会将完成的位图传回合成线程

画/Draw

合成线程最后会将计算出每个位图在屏幕上的位置,交由GPU最终呈现

关于reflow

reflow即重新计算Layout树
改动元素的宽高位置等可能会影响布局树的操作后需要重新计算布局树
为避免多次改动而重复计算布局树造成的性能问题,浏览器会合并这些操作,当js代码完成后再统一计算,这意味着reflow异步运行
这也带来一个问题,即当我们想要获取布局信息时无法获取到最新的布局信息
浏览器经过权衡过后决定更改属性的额reflow异步完成获取属性为立即执行

关于repaint

repaint即根据分层信息重新生成绘制指令
当改动了可见元素的样式后就会触发repaint
所以当触发了reflow后也会触发repaint

关于transform

transform效率高的原因是它只作用于整个渲染流程的最后一步draw
draw作用于合成线程中,不影响渲染主线程,同样渲染主线程也不会影响到合成线程

你可能感兴趣的:(网页,前端,css,html)