你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

前端性能优化(二)03-页面性能优化之浏览器——重排reflow与重绘repaint 导致重排的操作 只触发重绘不触发重排的CSS属性 减少重排与重绘

2021/12/16 0:51:41

前端性能优化(二)03-页面性能优化之浏览器——重排reflow与重绘repaint & 导致重排的操作 & 只触发重绘不触发重排的CSS属性 & 减少重排与重绘

重排reflow与重绘repaint

重排reflow

reflow指的是重新计算页面布局。

某个节点reflow时会重新计算节点的尺寸和位置,而且还有可能触发其子节点、祖先节点和页面上的其他节点reflow。在这之后再触发一次repaint。

当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流,每个页面至少需要一次回流,就是在页面第一次加载的时候。

导致reflow的操作

  • 调整窗口大小
  • 改变字体
  • 增加或者移除样式表
  • 内容变化,比如用户在input框中输入文字
  • 激活 CSS 伪类,比如 :hover (IE 中为兄弟结点伪类的激活)
  • 操作 class 属性
  • 脚本操作 DOM
  • 计算 offsetWidth 和 offsetHeight 属性
  • 设置 style 属性的值

触发页面重布局的一些css属性

  • 盒子模型相关属性会触发重布局
    • width
    • height
    • padding
    • margin
    • display
    • border-width
    • border
    • min-height
  • 定位属性及浮动也会触发重布局
    • top
    • bottom
    • left
    • right
    • position
    • float
    • clear
  • 改变节点内部文字结构也会触发重布局
    • text-align
    • overflow-y
    • font-weight
    • overflow
    • font-family
    • line-height
    • vertical-align
    • white-space
    • font-size

重绘repaint

repiant或者redraw遍历所有的节点检测各节点的可见性、颜色、轮廓等可见的样式属性,然后根据检测的结果更新页面的响应部分。

当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘

只触发重绘不触发重排的一些CSS属性:

  • color
  • border-styleborder-radius
  • visibility
  • text-decoration
  • backgroundbackground-imagebackground-positionbackground-repeatbackground-size
  • outlineoutline-coloroutline-styleoutline-width
  • box-shadow

我们来看一个例子:

var bstyle = document.body.style; // cache

bstyle.padding = "20px"; // reflow, repaint
bstyle.border = "10px solid red"; //  再一次的 reflow 和 repaint

bstyle.color = "blue"; // repaint
bstyle.backgroundColor = "#fad"; // repaint

bstyle.fontSize = "2em"; // reflow, repaint

// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('dude!'));

当然,我们的浏览器是聪明的,它不会像上面那样,你每改一次样式,它就reflow或repaint一次。一般来说,浏览器会把这样的操作积攒一批,然后做一次reflow,这又叫异步reflow或增量异步reflow。但是有些情况浏览器是不会这么做的,比如:resize窗口,改变了页面默认的字体,等。对于这些操作,浏览器会马上进行reflow。

但是有些时候,我们的脚本会阻止浏览器这么干,比如:如果我们请求下面的一些DOM值:

减少重绘与重排:

重绘和回流在实际开发中是很难避免的,我们能做的就是尽量减少这种行为的发生。

  • js尽量少访问dom节点和css 属性,尽量不要过多的频繁的去增加,修改,删除元素,因为这可能会频繁的导致页面reflow,可以先把该dom节点抽离到内存中进行复杂的操作然后再display到页面上。(虚拟DOM)

  • 减少不必要的 DOM 层级(DOM depth)。改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行 reflow 上面。

  • 不要通过父级来改变子元素样式,最好直接改变子元素样式,改变子元素样式尽可能不要影响父元素和兄弟元素的大小和尺寸

  • 尽量通过class来设计元素样式,切忌用style 多次操作单个属性

    // bad
    var left = 10,
    top = 10;
    el.style.left = left + "px";
    el.style.top  = top  + "px";
    
    // Good
    el.className += " theclassname";
    
    // Good
    el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
    
  • 尽可能的为产生动画的 HTML 元素使用 fixedabsoluteposition ,那么修改他们的 CSS 是不会 Reflow 的。

  • img标签要设置高宽,以减少重绘重排

  • 把DOM离线后修改,如将一个dom脱离文档流,比如display:none ,再修改属性,这里只发生一次回流。

  • 尽量用 transform 来做形变和位移,不会造成回流

  • 权衡速度的平滑。比如实现一个动画,以1个像素为单位移动这样最平滑,但reflow就会过于频繁,CPU很快就会被完全占用。如果以3个像素为单位移动就会好很多。

  • 不要用tables布局的另一个原因就是tables中某个元素一旦触发reflow就会导致table里所有的其它元素reflow。在适合用table的场合,可以设置table-layout为auto或fixed,

  • 避免不必要的复杂的 CSS 选择器,尤其是后代选择器(descendant selectors),因为为了匹配选择器将耗费更多的 CPU。

PS: display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。

总结一下:

重排:元素的尺寸变了、位置变了

重绘:元素的颜色、背景、边框、轮廓变了,但是,元素的几何尺寸没有变。

Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程是非常痛苦和耗电的。