写在前面
- 浏览器是使用流式布局模型
- 浏览器把HTML解析成DOM、CSS解析成CSSDOM。DOMTree和CSSTree结合产生了Render Tree
- Render Tree决定了所有节点的样式,然后计算他们在页面上的大小和位置,并绘制到页面上
- 对Render Tree的计算通常只需要遍历一次。但table及其内部元素除外。可能需要多次计算,这也是为什么要避免使用table布局的原因
一句话:
回流必然引起重绘、重绘不一定引起回流
重绘(Repaint)
当页面中元素的样式不会影响文档中的位置时(位置不变),浏览器会将新样式赋给元素,并重新绘制。这个过程叫重绘
引起重绘:color、background-color
重排(Reflow)
当部分或者全部元素的尺寸、结构、位置等改变时,浏览器重新渲染。这个过程叫回流
- 会导致回流的操作
- 浏览器首次渲染
- 浏览器窗口大小变化
- 元素尺寸、位置发生变化
- 字体大小变化
- 添加或删除元素
- 激活CSS伪类(如:hover)
- 查询某些元素或调用某些方法
一些常用会导致回流的属性和方法:(浏览器会立刻清空队列,进行重排)
- client属性(
clientWidth、clientHeight、clientTop、clientLeft) - offset属性(
offsetWidth、offsetHeight、offsetTop、offsetLeft) - scroll属性(
scrollWidth、scrollHeight、scrollTop、scrollLeft) 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),让它脱离文档流
- 可以考虑牺牲流畅度来换取性能