重绘和重排(Repaint & Reflow)

152 阅读2分钟

写在前面

  1. 浏览器是使用流式布局模型
  2. 浏览器把HTML解析成DOM、CSS解析成CSSDOM。DOMTree和CSSTree结合产生了Render Tree
  3. Render Tree决定了所有节点的样式,然后计算他们在页面上的大小和位置,并绘制到页面上
  4. 对Render Tree的计算通常只需要遍历一次。但table及其内部元素除外。可能需要多次计算,这也是为什么要避免使用table布局的原因 一句话:回流必然引起重绘、重绘不一定引起回流

重绘(Repaint)

当页面中元素的样式不会影响文档中的位置时(位置不变),浏览器会将新样式赋给元素,并重新绘制。这个过程叫重绘

引起重绘:color、background-color

重排(Reflow)

当部分或者全部元素的尺寸、结构、位置等改变时,浏览器重新渲染。这个过程叫回流

  • 会导致回流的操作
  • 浏览器首次渲染
  • 浏览器窗口大小变化
  • 元素尺寸、位置发生变化
  • 字体大小变化
  • 添加或删除元素
  • 激活CSS伪类(如:hover)
  • 查询某些元素或调用某些方法

一些常用会导致回流的属性和方法:(浏览器会立刻清空队列,进行重排)

  • client属性(clientWidthclientHeightclientTopclientLeft
  • offset属性(offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scroll属性(scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

如何避免(优化)

1.读写分离操作

div.style.left = "10px";
div.style.width = "12px";
console.info(div.offsetLeft)
console.info(div.offsetTop)

2.样式集中改变

// bad
var left = 10;
var top = 10;
el.style.left = left + "px"
el.style.top = top + "px"

// good 一次修改
el.style.cssText += `left: ${left};top: ${top}`
// good 直接修改class
el.className += "newClassName"

3.缓存布局

// bad 会触发两次重排
div.style.left = div.offsetLeft + 1 + "px";
div.style.top = div.offsetTop + 1 + "px";

// good 缓存布局信息 相当于读写分离.只会有一次重排
var curLeft = div.offsetLeft;
var curTop = div.offsetTop; // 前面没有设置,不会引起二次重排
div.style.left = curLeft + 1 + "px";
div.style.top = curTop + 1 + "px";

4.离线改变dom

  • 在要操作之前,通过display隐藏dom,当操作完成之后,才将元素显示出来。因为隐藏元素不会触发重绘和重排
dom.display = "none";
dom.display = "block";
  • 通过使用DocumentFragment创建一个dom碎片,在它上面批量操作dom。操作完成后再添加到页面中,只会触发一次重排

  • 复制节点,在副本上工作,然后替换它

5.动画优化

  • 对具有复杂动画的元素使用绝对定位(fixed、absolute),让它脱离文档流
  • 可以考虑牺牲流畅度来换取性能