重绘与回流:我做错了什么?

137 阅读4分钟

我正在参加「掘金·启航计划」

涅槃计划CSS篇

前言

hi大家好,我是小鱼,前几天做性能优化的时候组长偶然提到了重绘与回流这个东西,发现有些东西是我以前没有注意到的,今天就重新深入学习一遍,为什么CSS性能让JavaScript变慢

先了解一下输入url之后到看到页面的大致流程

  1. 用户输入域名,然后DNS解析成IP地址,过程
    • 浏览器缓存
    • 主机缓存
    • hosts文件
    • 路由器缓存
    • DNS缓存
    • DNS递归查询
  2. 浏览器根据IP地址请求服务器
  3. 服务器响应http请求,并返回给浏览器
  4. 浏览开始渲染

浏览器是如何渲染的

先看图

image.png

  1. 解析HTML文件,构建DOM Tree
    • 读取html文档,将字节转换成字符,确定tokens(标签),再将tokens转换成节点,以节点构建 DOM 树
    • 遇到任何样式(link、style)与脚本(script)都会阻塞(外部样式不阻塞后续外部脚本的加载)因为JS会对DOM节点进行操作,浏览器无法预测未来的DOM节点的具体内容,为了防止无效操作,节省资源,只能阻塞DOM树的构建。
  2. 解析CSS,构建CSSOM Tree
  3. 将DOM Tree和CSSOM Tree合并,构建Render tree(渲染树)
    • 结合DOM树和CSSOM树,生成一颗渲染树的过程称为Attachment。是页面可视化元素按照其显示顺序组成的树,可以让浏览器按照正确的顺序绘制内容。
  4. Layout(布局):根据生成的渲染树,进行Layout,得到节点的几何信息(位置,大小)
  5. Painting(绘制):根据渲染树以及布局得到的几何信息,得到节点的绝对像素绘制整个页面(painting)

重绘(repaint)

重绘字面意思就是重新绘制,重新绘画。 当渲染树某个节点发生改变,这个改变是指背景颜色、字体颜色等等发生改变,而不是空间上的位置或者大小发生改变,那么我们把这个改变叫做重绘(repaint)。

回流(reflow)

回流(也叫重排),当渲染树某个节点发生改变,这个改变是指影响了节点的几何属性(如宽、高、内边距、外边距、或是float、position、display:none;等等),导致这个节点在空间上的位置或者大小发生改变,浏览器需要重绘,重新生成渲染树重新布局,那么这个就叫做回流,

小小总结:回流必将引起重绘,而重绘不一定会引起回流

image.png

回流是如何引起的

  1. 调整窗口大小
  2. 改变字体
  3. 增加或者移除样式表
  4. 内容变化,比如用户在input框中输入文字
  5. 激活 CSS 伪类,比如 :hover (IE 中为兄弟结点伪类的激活)
  6. 操作 class 属性
  7. 脚本操作 DOM
  8. 计算 offsetWidth 和 offsetHeight 属性
  9. 设置 style 属性的值

如何避免回流

大家都知道回流比重绘的成本高很多,那么我们平时要如何避坑回流进而来优化浏览器的渲染过程呢?

1. DOM的增删行为

比如你要删除某个节点,给某个父元素增加子元素,这类操作都会引起回流。如果要加多个子元素,最好使用 documentfragment

2. 避免设置多层内联样式

通过style属性设置样式导致回流,避免设置多级内联样式,因为每个都会造成回流,样式应该合并在一个外部类,这样当该元素的class属性可被操控时仅会产生一个reflow。

3. 复杂动画效果应用到position属性为absolute或fixed的元素上

复杂的动画效果我们可以让他脱离文档流,这样它们就不影响其他元素的布局,所以它们只会导致重新绘制,而不是一个完整回流。这样消耗会更低。

4. 避免使用table布局

在布局完全建立之前,table是一个可以在它们之前已经进入的DOM元素的显示元素,因为有可能表格的最后一个单元格的内容过宽而导致纵列大小完全改变

5. 避免使用CSS的JavaScript表达式

避免使用CSS表达式(例如:calc()

6. 浏览器窗口尺寸改变

resize事件发生也会引起回流

end

希望大家对重绘与回流有一个更深入的了解,在平时工作上能够正确认识这些问题。文章如果有不对的地方也请大家指出来,互相学习。

image.png