前端性能:回流与重绘的学习

浏览器

  1. 浏览器使用流式布局模型
  2. 浏览器将html解析成DOM,把css解析成CSSOM,DOM和CSSOM合并产生渲染树RenderTree
  3. 根据RenderTree,浏览器计算元素在页面上的大小和位置,然后把节点绘制在页面上
  4. 流式布局的特点:一般对RenderTree遍历一次就可以完成页面渲染。但是table及其内部元素可能要进行多次运算才能完成渲染。

回流

当渲染树中部分或全部元素的尺寸、结构、边距等某些属性发生改变时,浏览器重新渲染部分或者全部文档的过程
导致回流的操作:

  • 页面首次渲染、浏览器窗口大小发生改变
  • 元素几何属性发生变化,尺寸或者位置发生改变
  • 元素内容发生改变,比如文字数量或者图片大小等
  • 元素字体大小变化(应该是不算c3?)
  • DOM树结构变化,添加或者删除可见的DOM元素
  • CSS伪类激活,例如hover(?真的吗)
  • 获取某些属性,offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE)。所以,在多次使用这些值时应进行缓存。

重绘

当元素的一部分属性发生变化,如color、background-color、visibility等,不会影响元素在文档流中的位置,不需要重新渲染,浏览器只是将新样式赋给元素并重新绘制它

var s = document.body.style;
s.padding = "2px"; // 回流+重绘
s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 再一次重绘
s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
// 添加node,再一次 回流+重绘
document.body.appendChild(document.createTextNode('abc!'));

如何避免

浏览器应对机制:队列中的操作到了一定的数量或者到了一定的时间间隔,就会flush队列,进行批量处理。

  • 控制
    尽量减少repaint和reflow的次数,并且repaint对性能的影响小于reflow。
  • 脱离
    动画时时刻刻都在操作着DOM,为了避免动画使得其他节点也在时时刻刻reflow,可以将动画所在的元素设为position: fixed或者position: absolute,使其脱离文档流,这个元素reflow时不会影响其他节点的布局,虽然还是会产生repaint,但相对来说得到了优化。
  • 合并
    能一次完成的操作就不要分两次。添加多个节点时使用DocumentFragment,处理完之后再一起更新:
var docFragm = document.createDocumentFragment();
var elem, contents;
for(var i = 0; i < textlist.length; i++) {
    elem = document.createElement('p');
    contents = document.createTextNode(textlist[i]);
    elem.appendChild(contents);
    docFragm.appendChild(elem);
}
document.body.appendChild(docFragm);
  • 复制
    对复制品进行操作,对已有节点进行DOM操作使用cloneNode():
var original = document.getElementById('container');
var cloned = original.cloneNode(true);
cloned.setAttribute('width', '50%');
var elem, contents;
for(var i = 0; i < textlist.length; i++) {
    elem = document.createElement('p');
    contents = document.createTextNode(textlist[i]);
    elem.appendChild(contents);
    cloned.appendChild(elem);
}
//先复制,再进行替代
original.parentNode.replaceChild(cloned, original);

使用cloneNode()要注意的是,唯一的参数代表是否进行深复制。另外cloneNode()无法复制事件监听函数,以及表单控件的value。
又比如获取offsetWidth,getComputedStyle()这些取值操作每次都会触发reflow,在第一次调用时存起来也是复制的一种。

  • 舍弃
    在做好了其他优化措施的前提下想要进一步提升性能有时需要舍弃,比如减小动画帧数,即增大动画函数执行的间隔,这样动画流畅程度会降低,但整个应用的性能得到了提升。
  • 分类归纳

CSS:

  1. 避免使用table布局。
  2. 尽可能在DOM树的最末端改变class。
  3. 避免设置多层内联样式。
  4. 将动画效果应用到position属性为absolute或fixed的元素上。
  5. 避免使用CSS表达式(例如:calc())。

javascript:

  1. 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
  2. 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
  3. 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
  4. 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  5. 对具有复杂动画的元素使用position:absolute,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

回流一定产生重绘,重绘不一定产生回流

posted @ 2020-06-04 19:47  vera-7c  阅读(391)  评论(0编辑  收藏  举报