回流(Reflow)重绘(Repaint) 什么时候会触发回流或重绘呢?
- 当我们对dom 进行修改当时候会引发它外观(样式)上的改变时,就会触发回流或重绘。
- 这个过程本质上还是因为我们对 DOM 的修改触发了渲染树(Render Tree)的变化所导致的
浏览器渲染页面的流程
- 1.根据 HTML 结构生成 DOM 树
- 根据 CSS 生成 CSSOM
- 将 DOM 和 CSSOM 整合形成 RenderTree
- 根据 RenderTree (渲染树)开始渲染和展示
- 遇到<script>时,会执行并阻塞渲染
回流(reflow):
-
当
Render Tree中部分或全部, 因元素的尺寸、布局、隐藏等改变而需要重新构建,浏览器重新渲染的过程称为 回流。 -
会导致回流的操作:
- 页面首次渲染。
- 浏览器窗口大小发生改变。
- 元素尺寸或者位置发生改变。
- 元素内容变化(文字数量或者图片大小发生改变)。
- 元素字体大小的改变。
- 添加或者删除可见的
DOM元素。 - 激活
CSS伪类 (eg::hover)。 - 查询某些属性或调用某些方法。
-
一些常用且会导致回流的属性和方法。
clientWidth、clientHeight、clientTop、clientLeftoffsetWidth、offsetHeight、offsetTop、offsetLeftscrollWidth、scrollHeight、scrollTop、scrollLeftscrollIntoView()、scrollIntoViewIfNeeded()getComputedStyle()getBoundingClientRect()scrollTo()
重绘(repaint)
- 当页面中元素样式的改变并不影响b布局时(eg:
color、background-color等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
回流重绘总结
- 由此来看 重绘不一定导致回流,回流一定会导致重绘 前面我们说回流和重绘是会对dom进行修改,会消耗性能,所以我们要尽可能减少回流和重绘的次数。
那如何避免回流和重绘
-
减少对 render tree 的操作 【合并多次多DOM和样式的修改】
-
减少对一些style信息的请求,尽量利用好浏览器的优化策略
-
CSS
- 直接改变className. (把要修改的样式集中到一个 class 内统一修改);
- 避免使用
table布局 (尽量不要使用表格布局,如果没有定宽表格一列的宽度由最宽的一列决定,那么很可能在最后一行的宽度超出之前的列宽,引起整体回流造成table可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。) - 尽可能在DOM树的最末端改变class,尽可能在DOM树的里面改变class(可以限制回流的范围)
- 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位;
- 使用display:none技术,只引发两次回流和重绘;
-
JS
-
避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。(而不是利用js控制样式)
-
不要经常访问会引起浏览器缓存队列的属性(上述那些浏览器会立刻清空队列的属性)。如果确实要访问,利用缓存。eg:
// bad for (var i = 0; i < len; i++) { el.style.left = el.offsetLeft + x + "px"; el.style.top = el.offsetTop + y + "px"; } // good var x = el.offsetLeft, y = el.offsetTop; for (var i = 0; i < len; i++) { x += 10; y += 10; el.style = x + "px"; el.style = y + "px"; } -
尽量将需要改变DOM的操作一次完成
const container = document.getElementById('container') container.style.width = '100px' container.style.height = '200px' container.style.border = '10px solid red' container.style.color = 'red' //将这段代码用 类名去合并 .basic_style { width: 100px; height: 200px; border: 10px solid red; color: red; } const container = document.getElementById('container') container.classList.add('basic_style') -
对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
-
避免频繁操作
DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。**DocumentFragment**,文档片段接口,表示一个没有父级文件的最小文档对象. 与Document最大的区别是因为DocumentFragment不是真实DOM树的一部分,它的变化不会触发DOM树的(重新渲染) ,且不会导致性能等问题。
-