重绘与回流

重绘和回流(重排)

回流必将引起重绘,重绘不一定会引起回流

浏览器的渲染机制(页面生成过程)

'浏览器渲染机制'

  • HTML 解析器将 HTML 解析成 DOM 树

    DOM 树里包含了所有的 HTML 标签,
    包括 dispaly:none;隐藏的元素,以及 JS 动态添加的元素

  • CSS 解析器将 CSS 解析成 CSSOM 树

  • DOM 树与 CSSOM 树结合,生成渲染树(Render Tree),这一过程称为 Attachment

  • Layout:在生成渲染树的基础上运行布局以计算每个节点的几何信息(位置,大小等)

    第一次确定节点的大小和位置称为布局。
    随后对节点大小和位置的重新计算称为回流。

  • 重绘(Painting):根据渲染树以及回流得到的几何信息,得到节点的绝对像素

  • Display:将像素发送给 GPU,展示在页面上

生成渲染树(Render Tree)

生成渲染树

CSSOM 与 DOM 构成渲染树,浏览器完成工作: 1.从 DOM 树的根节点开始遍历每个可见节点 2.对于每个可见节点,找到 CSSOM 树中对应的规则并应用 3.根据每个可见节点以及对应的样式,组合生成渲染树

RenderTree 不同于 DOM 树,因为 RenderTree 中的每个 NODE 都有自己的 style,
并且 Render 树只会包含可见的节点,不包含隐藏的节点。

注意:渲染树只包括可见的节点

不可见节点

遍历 DOM 树生成 RenderTree 只会遍历可见节点,什么是不可见节点呢?

  • 不会渲染输出的节点,如 head、script、me tameta、link 等
  • 使用 css 隐藏的节点。例如display:none;

    注意:使用visibility: hidden;opacity隐藏的节点的节点会出现在 Render 树上,因为它们会占用空间。
    只有 display:none;的节点才不会显示在渲染树上

回流(重排)

当 DOM 的变化影响了元素的几何信息(元素的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其放在界面中正确的位置,这个过程称为重排。

重排也叫回流,简单的说也就是重新生成布局,重新排列。

导致回流的操作

1.页面首次渲染,这是开销最大的一次重排
2.添加/删除可见DOM元素
3.浏览器窗口大小发生改变
4.元素尺寸(比如边距、填充、边框、宽度和高度等)或者位置发生改变
5.改变元素字体大小
6.改变元素内容(文字数量或图片大小等等)
7.激活 CSS 伪类(例如::hover)
8.查询某些属性或调用某些方法

  • 一些常用且会导致回流的属性和方法:
    • clientWidth、clientHeight、clientTop、clientLeft
    • offsetWidth、offsetHeight、offsetTop、offsetLeft
    • scrollWidth、scrollHeight、scrollTop、scrollLeft
    • scrollIntoView()、scrollIntoViewIfNeeded()
    • getComputedStyle()
    • getBoundingClientRect()
    • scrollTo()

重绘

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility 等),没有改变布局,浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

常见引起重绘的属性

属性 Description Test Text Test Text
color border-style visibility background
text-decoration background-image background-position background-repeat
outline-color outline outline-style border-radius
outline-width box-shadow background-size

性能影响

回流比重绘代价更高
有时即使仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。

  • 两者都会影响性能,但是重排比重绘工作量大,回使我们付出更多的性能代价
  • 回流必将引起重绘,重绘不一定会引起回流
    • 重绘单单改变元素的外观,不会引起网页重新生成布局
    • 当浏览器完成重排后,将会重新绘制收到此次重排影响的部分。

浏览器的优化机制

回流比重绘的代价更高,有时仅仅只是回流一个元素,他的父元素以及任何跟随的它的元素也会造成回流;
由于每次重排都会造成额外的计算消耗,现代浏览器会对频繁的重绘和回流操作进行优化:

浏览器会维护一个队列处理重绘和回流,将所有引起重绘和回流的操作放入队列中;当队列中的任务数量或者时间达到一个阙值,会清空队列中的任务,进行批处理,这样就能将多次的回流和重绘变成一次。
但是,当你获取布局信息的时候,会强制队列刷新,触发重绘和回流来获取正确的值。例如访问以下的属性或者方法:

  • offsetTop、offsetLeft、 offsetWidth、offsetHeight、
  • scrollTop、scrollLeft、scrollWidth、scrollHeight、
  • clientTop、clientLeft、clientWidth、clientHeight
  • width、height
  • getComputedStyle()
  • getBoundingClientRect()

因为队列中可能会有影响这些属性或方法返回值的操作,所以浏览器会强行清空队列,保证你能拿到最精确的值。

如何避免

CSS

  • 避免使用 table 布局。

    减少重排的范围,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算

  • 尽可能在 DOM 树的最末端改变 class。
  • 避免设置多层内联样式。
  • 将动画效果应用到 position 属性为 absolute 或 fixed 的元素上。
  • 避免使用 CSS 表达式(例如:calc())。

JS

  • 避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。

    合并多次对DOM和样式的修改:
    使用cssText
    修改CSS的class

  • 避免频繁操作 DOM,创建一个文档片段( documentFragment),在当前DOM之外构建一个子树,在它上面应用所有 DOM 操作,最后再把它添加到文档中。

  • 也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不 会引发回流和重绘。

    少回流重绘次数: 1.使元素脱离文档流 2.对其进行多次修改 3.将元素带回到文档中。
    元素设置 display: none 之后,对 DOM 的所有修改都不会引起回流,因为它已经不在渲染树了。

  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。

  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

参考文档

posted @ 2022-08-20 17:03  Djjdb  阅读(76)  评论(0)    收藏  举报