【译】重绘与回流no-no篇

触发reflow(回流)和repaint(重绘)

简单回顾下回流和重绘的定义

  • 回流:主要是计算位置和大小
  • 重绘:把内容画(更新)到屏幕上

注意:回流必定会触发重绘,而重绘不必定会回流javascript

渲染树发生变化,就会产生回流或重绘,例如:php

  • DOM节点的增删改
  • 隐藏一个DOM节点,用display:none(回流和重绘都会触发),visibility: hidden(只有重绘,由于没有几何变化)
  • 在页面上移动DOM节点
  • 增长或修改样式
  • 改变浏览器大小,改变字体大小,甚至滚动页面!

再看一些例子:css

var bstyle = document.body.style;
 
bstyle.padding = "20px"; // 回流,重绘
bstyle.border = "10px solid red"; // 再一次回流和重绘
 
bstyle.color = "blue"; // 只有重绘,没有颜色变化
bstyle.backgroundColor = "#fad"; // 重绘
 
bstyle.fontSize = "2em"; // 回流,重绘
 
// 新加元素 - 回流,重绘
document.body.appendChild(document.createTextNode('dude!'));
复制代码

还有些回流会带来更多的性能损耗,好比你把页面顶部的一个div设置了动画或者拉大了,致使页面下面其余部分都下去了。java

浏览器是聪明的

由于渲染树的回流和重绘比较损耗,浏览器目标在于减少负面影响。一个策略就是根本不作这件事情,至少如今不作。浏览器把你写的更改放在一个队列里而后批量执行。用这种方法将会把屡次须要回流的更改合成一次回流进行计算。浏览器可以把屡次更改放进队列,而后隔一段时间或者达到必定数量时一次性处理掉。git

可是有时候,脚本(js)可能会阻止浏览器这项优化措施,致使它(立刻)清理队列以及执行全部更改。这件事发生在你修改样式信息,好比:github

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. getComputedStyle(), 或者 currentStyle in IE
  5. 更多

以上这些主要是获取某个节点的样式信息的,一旦你调用这些,浏览器都须要给你最新的值。为了这样作,浏览器就须要执行全部计划中的更改,清理队列执行回流。浏览器

举个例子,快速连续地获取和设置样式(循环中),好比:app

// no-no!
el.style.left = el.offsetLeft + 10 + "px";
复制代码

减小重绘和回流

几条建议以下:oop

  • 不要一行一行单独地修改样式。最好是直接修改class而不是修改样式,这样也比较可维护,不够这个只对于静态样式而言。若是样式是动态,那最好是用cssText去作。
// bad
var left = 10,
    top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";
 
// better 
el.className += " theclassname";
 
// 或者须要动态的修改top和left...
// better
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
复制代码
  • 以“离线”方式批量处理DOM更改。离线意思是不在真实的DOM树下(作更改),你能够:性能

    • 使用documentFragment去临时存一下更改
    • 克隆一个节点出来,而后在这个克隆的节点上作修改,而后在和原来的节点作替换
    • 先用display:none(须要一次回流及重绘)隐藏掉元素,增长100次修改,而后再显示(再花费一次回流重绘)。这样你就拿2次回流和重绘换掉了100次。
  • 不要过度地使用computed styles。若是你须要使用,那就拿一次,而后保存到本地变量里,并对这个本地变量进行操做。从新看下那个no-no例子:

// no-no!
for(big; loop; here) {
    el.style.left = el.offsetLeft + 10 + "px";
    el.style.top  = el.offsetTop  + 10 + "px";
}
 
// better
var left = el.offsetLeft,
    top  = el.offsetTop
    esty = el.style;
for(big; loop; here) {
    left += 10;
    top  += 10;
    esty.left = left + "px";
    esty.top  = top  + "px";
}
复制代码
  • 大致上,在你作了更改以后,须要考虑一下渲染树里有多少(节点)须要从新验证。好比,使用绝对定位(absolute positioning)把一个节点做为渲染树中的一个主体节点,那么给这个节点设置动画的时候,不会影响太多别的节点,一些在(动画涉及到的)区域内节点可能须要重绘,可是它们不须要回流。

原文

相关文章
相关标签/搜索