了解了浏览器渲染原理之后,我们知道了浏览器听过渲染树计算布局后,就开始绘制页面元素,但是渲染树并不是一成不变的,在我们的脚本当中它是可能改变的。
什么是重排和重绘
重排:若渲染树的一部分更新,且尺寸变化,就会发生重排,可以理解为渲染树需要重新计算;重排会从 <html> 这个 root frame 开始递归往下,依次计算所有的结点几何尺寸和位置。
重绘:一个元素外观的改变所触发的浏览器行为,例如改变visibility、outline、背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随回流。
重排何时会发生
(1)增加或删除DOM节点;
(2)display:none(重排并重绘);visibility:hidden(重绘);
(3)移动页面中的元素;
(4)改变元素尺寸(宽、高、内外边距、边框等);
(5)用户改变窗口大小,滚动页面等;
(6)页面初始渲染;
(7) 改变元素内容(文本或图片等);
浏览器的优化:渲染队列
重排只要出现,就会重新对DOM树进行渲染,而大多数时候,这种全局性的渲染是没必要的,不管页面发生了重绘还是重排,次数多了,性能就差。
比如我们想用js中修改一个div元素的样式,写下了以下代码
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
我们修改了元素的left、top、width、height属性,满足我们发生重排的条件,理论上会发生4次重排,但是实际上只会发生1次重排。
这是因为我们现代的浏览器都有渲染队列的机制,当我改变了元素的一个样式会导致浏览器发生重排或重绘时,它会进入一个渲染队列,然后浏览器继续往下看,如果下面还有样式修改,那么同样入队,直到下面没有样式修改,浏览器会按照渲染队列批量执行来优化重排过程,一并修改样式
这样就把本该4次的重排优化为1次。
但是我们现在想要修改样式后在控制台打印:
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);
这样写的话就会发生4次重排,因为offsetLeft/Top/Width/Height非常叼
它们会强制刷新队列要求样式修改任务立刻执行,毕竟浏览器不确定在接下来的代码中你是否还会修改同样的样式,为了保证获得正确的值,它不得不立刻执行渲染队列触发重排。
以下属性或方法会刷新渲染队列:
offsetTop、offsetLeft、offsetWidth、offsetHeight
clientTop、clientLeft、clientWidth、clientHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
getComputedStyle()(IE中currentStyle)
我们在修改样式过程中,要尽量避免使用上面的属性。
如何减少重绘和重排以提升页面性能
(1)不要一个个修改属性,应通过一个class来修改
错误写法:
div.style.width="50px";div.style.top="60px";
正确写法:
div.className+=" modify";
(2)clone节点,在副本中修改,然后直接替换当前的节点;
(3)若要频繁获取计算后的样式,请暂存起来;
(4)前面说到,回流的危害在于重新对DOM树进行渲染,那么,脱离文档流之后,进行的任何操作,都不会造成回流了!如果有需要经常进行复杂操作的地方,使用position:absolute/fixed定位,使之脱离文档流后进行操作,或者使用display:none,操作完成后再进入到文档流之中。
(5)批量添加DOM:多个DOM插入或修改,应组成一个长的字符串后一次性放入DOM。使用innerHTML永远比DOM操作快。(特别注意:innerHTML不会执行字符串中的嵌入脚本,因此不会产生XSS漏洞)。
(6)以下这些属性,只要是改动了他们的值,就会造成回流,建议将他们合并到一起操作,可以减少回流的次数。这些属性包括:offsetTop、offsetLeft、 offsetWidth、offsetHeight;scrollTop、scrollLeft、scrollWidth、scrollHeight;clientTop、clientLeft、clientWidth、clientHeight;getComputedStyle() 、currentStyle()。
网友评论