浏览器的渲染过程及回流、重绘

浏览器的渲染

理解:
一个渲染引擎主要包括:HTML解析器,CSS解析器,javascript引擎,布局layout模块,绘图模块…等等

浏览器渲染页面的整个过程:浏览器会从上到下解析文档。

  1. 遇见 HTML 标记,调用HTML解析器解析为对应的 token (一个token就是一个标签文本的序列化)并构建 DOM 树(就是一块内存,保存着tokens,建立它们之间的关系)。
  2. 遇见 style/link 标记调用相应解析器处理CSS标记,并构建出CSS样式树。
  3. 遇见 script 标记 调用javascript引擎 处理script标记、绑定事件、修改DOM树/CSS树等
  4. 将 DOM树 与 CSS树 合并成一个渲染树。
  5. 根据渲染树来渲染,以计算每个节点的几何信息(这一过程需要依赖GPU)。
  6. 最终将各个节点绘制到屏幕上。

以上这些模块依赖很多其他的基础模块,包括要使用到网络 存储 2D/3D图像 音频视频解码器 和 图片解码器。
所以渲染引擎中还会包括如何使用这些依赖模块的部分。

阻塞渲染

1.关于css阻塞:
声明:只有link引入的外部css才能够产生阻塞。
style标签中的样式:

  1. 由html解析器进行解析;
  2. 不阻塞浏览器渲染(可能会产生“闪屏现象”);
  3. 不阻塞DOM解析;

link引入的外部css样式(推荐使用的方式):

  1. 由CSS解析器进行解析。
  2. 阻塞浏览器渲染(可以利用这种阻塞避免“闪屏现象”)。
  3. 阻塞其后面的js语句的执行
  4. 不阻塞DOM的解析(绝大多数浏览器的工作方式):

优化核心理念:尽可能快的提高外部css加载速度

  1. 使用CDN节点进行外部资源加速。
  2. 对css进行压缩(利用打包工具,比如webpack,gulp等)。
  3. 减少http请求数,将多个css文件合并。
  4. 优化样式表的代码

2.关于js阻塞:
阻塞后续DOM解析:

原因:浏览器不知道后续脚本的内容,如果先去解析了下面的DOM,而随后的js删除了后面所有的DOM,那么浏览器就做了无用功,浏览器无法预估脚本里面具体做了什么操作,例如像document.write这种操作,索性全部停住,等脚本执行完了,浏览器再继续向下解析DOM。

阻塞页面渲染:

原因:js中也可以给DOM设置样式,浏览器等该脚本执行完毕,渲染出一个最终结果,避免做无用功。

阻塞后续js的执行:

原因:维护依赖关系,例如:必须先引入jQuery再引入bootstrap

3.备注事项:

  1. css的解析和js的执行是互斥的(互相排斥),css解析的时候js停止执行,js执行的时候css停止解析。
  2. 无论css阻塞,还是js阻塞,都不会阻塞浏览器加载外部资源(图片、视频、样式、脚本等)

原因:
浏览器始终处于一种:“先把请求发出去”的工作模式,只要是涉及到网络请求的内容,无论是:图片、样式、脚本,都会先发送请求去获取资源,至于资源到本地之后什么时候用,由浏览器自己协调。这种做法效率很高。

  1. WebKit 和 Firefox 都进行了【预解析】这项优化

在执行js脚本时,浏览器的其他线程会预解析文档的其余部分,
找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树

总结:

  • 在上述的过程中,网页在加载和渲染过程中会触发“DOMContentLoaded”和“onload”事件

  • 分别是在DOM树构建(解析)完成之后,以及DOM树构建完并且网页所依赖的资源都加载完之后

  • 上面介绍的是一个完整的渲染过程,但现代网页很多都是动态的,这意味着在渲染完成之后,由于网页的动画或者用户的交互

  • 浏览器其实一直在不停地重复执行渲染过程。(重绘重排),以上的数字表示的是基本顺序,这不是严格一致的,这个过程可能重复也可能交叉

css图层

浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。
在渲染DOM的时候,浏览器所做的工作实际上是:

  1. 获取DOM后分割为多个图层
  2. 对每个图层的节点计算样式结果 (Recalculate style–样式重计算)
  3. 为每个节点生成图形和位置 (Layout–布局,重排,回流)
  4. 将每个节点绘制填充到图层位图中 (Paint–重绘)
  5. 图层作为纹理上传至GPU
  6. 组合多个图层到页面上生成最终屏幕图像 (Composite Layers–图层重组)

图层创建的条件

Chrome浏览器满足以下任意情况就会创建图层:

  1. 拥有具有3D变换的CSS属性
  2. 使用加速视频解码的video标签节点
  3. canvas标签节点
  4. CSS3动画的节点
  5. 拥有CSS加速属性的元素(will-change)

重绘(Repaint)

  1. 重绘是一个元素外观的改变所触发的浏览器行为,例如改变outline、背景色等属性。
  2. 浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,所以并不一定伴随重排。

需要注意的是:

  1. 重绘重排都是以图层为单位,如果图层中某个元素需要重绘,那么整个图层都需要重绘。
  2. 所以为了提高性能,我们应该让这些“变化的东西”拥有一个自己一个图层, 不过好在绝大多数的浏览器自己会为CSS3动画的节点自动创建图层。

重排(Reflow 又称:回流)

渲染对象在创建完成并添加到渲染树时,并不包含位置和大小信息。计算这些值的过程称为布局或重排

  1. “重绘"不一定需要"重排”,比如改变某个网页元素的颜色,就只会触发"重绘",不会触发"重排",因为布局没有改变。
  2. “重排"大多数情况下会导致"重绘”,比如改变一个网页元素的位置,就会同时触发"重排"和"重绘",因为布局改变了。

常见的触发重排的操作

  • Reflow(重排) 的成本比 Repaint(重绘) 的成本高很多很多。
  • 一个结点的 Reflow很有可能导致子结点,甚至父点以及同级结点的 Reflow。
  • 在一些高性能的电脑上也许还没什么,但是如果 Reflow发生在手机上,那么这个过程是非常痛苦和耗电的。
  1. 当你增加、删除、修改 DOM 结点时,会导致 Reflow , Repaint。
  2. 当你移动 DOM 的位置 当你修改 CSS样式的时候。
  3. 当你 Resize 窗口的时候(移动端没有这个问题,因为移动端的缩放没有影响布局视口)
  4. 当你修改网页的默认字体时。
  5. 【获取某些属性时(width,height…)!!!!!】

注:display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发生位置变化。

优化方案(重绘重排)

  1. 元素位置移动变换时尽量使用CSS3的transform来代替对top left等的操作变换(transform)和透明度(opacity)的改变仅仅影响图层的组合
  2. 使用opacity来代替visibility,opacity配合图层使用,即不触发重绘也不触发重排
  3. 不要使用table布局
  4. 将【多次改变样式属性的操作合并成一次】操作

不要一条一条地修改DOM的样式,预先定义好class,然后修改DOM的className

  1. 【将DOM离线后再修改】
  • 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。
  • 如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。
  1. 【利用文档碎片】(documentFragment)------vue使用了该种方式提升性能。

  2. 【不要把获取某些DOM节点的属性值放在一个循环里当成循环的变量】

当你请求向浏览器请求一些 style信息的时候,就会让浏览器flush队列,比如:
1. offsetTop, offsetLeft, offsetWidth, offsetHeight
2. scrollTop/Left/Width/Height
3. clientTop/Left/Width/Height
4. width,height
当你请求上面的一些属性的时候,浏览器为了给你最精确的值,需要刷新内部队列,
因为队列中可能会有影响到这些值的操作。即使你获取元素的布局和样式信息跟最近发生或改变的布局信息无关,
浏览器都会强行刷新渲染队列。

  1. 动画实现过程中,启用GPU硬件加速:transform: tranlateZ(0)
  2. 为动画元素新建图层,提高动画元素的z-index
  3. 编写动画时,尽量使用如下的API

requestAnimationFrame----请求动画帧

1.window.requestAnimationFrame() 
    说明:该方法会告诉浏览器在下一次重绘重排之前调用你所指定的函数
    1.参数:该方法使用一个回调函数作为参数,这个回调函数会在浏览器下一次重绘之前调用。
            回调函数会被自动传入一个参数,DOMHighResTimeStamp,标识requestAnimationFrame()开始触发回调函数的当前时间

    2.返回值:
            一个 long 整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。
            
备注:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()
 
2.window.cancelAnimationFrame(requestID)
    取消一个先前通过调用window.requestAnimationFrame()方法添加到计划中的动画帧请求。
    requestID是先前调用window.requestAnimationFrame()方法时返回的值,它是一个时间标识,用法与定时器的id类似。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回流(reflow)和重绘(repaint)是浏览器渲染页面时的两个重要概念。 回流是指当 DOM 的变化影响了元素的布局,需要重新计算元素的位置和大小,然后重新构建渲染树,最后进行绘制的过程回流可能会导致一些性能问题,因为它需要计算布局,这是一项非常耗费资源的操作。触发回流的操作包括: - 添加、删除或修改 DOM 元素 - 改变元素的位置、大小 - 改变元素的内容、文字 - 改变窗口大小或滚动 - 修改样式(尤其是影响元素盒模型的样式,例如 width、height、padding、margin 等) 重绘是指当渲染树中的元素样式改变,但没有影响它们的布局时,浏览器只需要重新绘制这些元素的内容,而不需要重新计算它们的位置和大小。重绘回流的开销要小一些,但也需要花费一定的时间。触发重绘的操作包括: - 改变元素的颜色、背景色、边框颜色等 - 改变元素的文本颜色、字体大小等 - 添加、删除、改变元素的阴影、透明度等 因此,为了提高页面性能,我们应该尽可能地减少回流重绘的次数。可以采取的措施包括: - 避免频繁读取和修改 DOM 元素的样式和属性,可以使用缓存或批量修改的方式来优化 - 使用 CSS3 动画代替 JavaScript 实现动画效果,因为 CSS3 动画可以利用硬件加速,而 JavaScript 实现的动画会导致频繁的回流重绘 - 将元素的 position 属性设置为 fixed 或 absolute,因为这些元素不会影响其他元素的布局,可以减少回流的次数 - 将元素的 opacity 属性设置为 0 或 1,而不是使用 visibility:hidden 或 display:none,因为前者只会触发重绘,而后者会触发回流

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值