重绘不一定会引起回流,而回流一定会引起重绘。
重绘
当元素的一部分属性发生改变并不影响它在文档流中的位置(即不会引起布局的变化)时,浏览器会将新样式赋予元素并重新绘制它,这个过程称为重绘。
常见的会引起重绘的属性有:
color background outline
border-style background-image outline-color
border-radius background-size outline-style
visibility background-repeat outline-width
text-decoration background-position box-shadow
回流
当render tree中的一部分或全部元素因为规模尺寸、布局、隐藏等改变而需要浏览器重新计算位置和大小,这个过程称为回流。
常见的会导致回流的操作:
页面初次渲染
浏览器窗口大小发生改变
元素尺寸或位置发生改变
元素字体大小变化
元素内容发生变化(文字数量或图片大小改变等, 如: 用户在input中输入内容)
添加或者删除可见的DOM元素
操作class属性
设置style属性
激活伪类(如::hover)
查询某些属性或调用某些方法
常见的会引起回流的属性:
width position overflow
height float overflow-y
min-height left font-weight
margin right font-family
padding top white-space
border bottom text-align
border-width clear vertival-align
display line-height
js中常见的会引起回流的属性和方法:
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()
scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()
减少页面重绘和回流的方法:
css:
尽量使用css属性简写:如:用boder代替boder-width,boder-style,boder-color;
尽量避免用table布局,因为可能很小的一个小改动会造成整个 table 的重新布局;
避免设置多层内联样式;
尽可能在DOM树的最末端改变class, 减小回流的范围;
将动画效果应用到position属性为absolute或fixed的元素上,让它脱离文档流,相当于建了一个新的文档对象,触发回流的是新建的文档对象,否则会引起父元素及后续元素频繁回流;
避免使用CSS表达式(例如:calc()),因为每次调用都会重新计算和加载页面;
为动画元素创建新图层,提高动画元素的z-index;
opacity配合图层使用,既不触发重排也不触发重绘;
js:
避免频繁操作样式,最好一次性重写style属性。或者可以先定义好 css 的 class,然后修改 DOM 的 className;
避免频繁操作DOM,可以使用文档碎片,将所有添加内容放到容器内,再添加到页面,这样只会触发一次回流和重构。
let fragment=document.createDocumentFragment();
fragment.appendChild(DOM对象);
document.body.appendChild(fragment);
3.可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘;
el.style.display = 'none';
...一些对el的操作
el.style.display = 'block';
4.可以先让元素脱离文档流,操作完之后再重新放回去;
el.style.position='absoulte';
...一些对el的操作
el.style.position='static'
5.如果要用(el.style.)的方式对一个元素设置多个属性,可以使用(el.style.cssText='...')合并成一个;
6.避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,可以用一个变量缓存起来;
7.可以克隆要操作的节点,对克隆之后的节点操作完成后,再替换回去;
const ul = document.getElementById('test');
const clone = ul.cloneNode(true);
...对clone的一些操作
ul.parentNode.replaceChild(clone, ul);