再论重排和重绘(浏览器的API)
重排一定会产生重绘,重绘发生在元素的可见的外观被改变,但并没有影响到布局的时候。比如,仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局)。
从根本上说,因为重排和重绘才实现了页面的状态改变。但重排和重绘是有代价的,作为开发工程师要做的事是尽可能小的代价实现页面dom的改变。
在《高性能JavaScript》里面有几下几个优化方法。
(1)我们要减少重绘和重排就是要减少对渲染树的操作,则我们可以合并多次的DOM和样式的修改。并减少对style样式的请求。直接改变元素的className,或者使用csstext
(2)display:none;先设置元素为display:none;然后进行页面布局等操作;设置完成后将元素设置为display:block;这样的话就只引发两次重绘和重排;
(3)缓存布局信息
不要经常访问浏览器的flush队列属性;如果一定要访问,可以利用缓存。将访问的值存储起来,接下来使用就不会再引发回流;
例如myElement元素沿对角线移动,每次移动一个像素。到500*500像素的位置结束。timeout循环体中可以这么做
myElement.style.left = 1 + myElement.offsetLeft + 'px'; myElement.style.top = 1 + myElement.offsetTop + 'px'; if(myElement.offsetLeft >= 500){ stopAnimation(); }
显然这种方法低效,每次移动都要查询偏移量,导致浏览器刷新渲染队列而不利于优化。好的办法是获取一次起始位置的值,然后赋值给一个变量。如下
var current = myElement.offsetLeft; current++; myElement.style.left = current + 'px'; myElement.style.top = current + 'px'; if(myElement.offsetLeft >= 500){ stopAnimation(); }
(4)使用cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;
let old = document.querySelector('#mylist'); let clone = old.cloneNode(true); appendNode(clone, data); old.parentNode.replaceChild(clone, old);
(5)将需要多次重排的元素,position属性设为absolute或fixed,元素脱离了文档流,它的变化不会影响到其他元素;
(6)如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document;
var fragment = document.createDocumentFragment(); var li = document.createElement('li'); li.innerHTML = 'apple'; fragment.appendChild(li); var li = document.createElement('li'); li.innerHTML = 'watermelon'; fragment.appendChild(li); document.getElementById('fruit').appendChild(fragment);
(7)尽量不要使用table布局。
(8)使用现代化的框架
(9)css前置于js
最后2条是我加的。
我站在山顶看风景!下面是我的家乡!