回流和重绘

浏览器渲染机制

  1. 解析HTML,生成DOM树,解析CSS,生成CSSOM
  2. DOM树和CSSOM树结合,生成渲染树Render Tree
  3. Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
  4. Paint(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  5. Display:将像素发送给GPU,展示在页面上。

为了构建渲染树,浏览器主要完成了以下工作:

  1. DOM树的根节点开始遍历每个可见节点
  2. 对于每个可见节点,找到CSSDOM树中对应的规则,并应用它们
  3. 根据每个可见节点以及其对应的样式,组合生成渲染树

第一步中,既然要遍历可见节点,那么我们要先知道,什么节点是不可见的。不可见的节点包括:

  1. 一些不会渲染输出的节点,比如scriptmetalink
  2. 一些通过css进行隐藏的节点。比如display:none。注意利用visibityopacity隐藏的节点。

总结:回流会引起重绘,重绘不一定会引起回流

回流reflow

当我们render tree中的一些元素的结构或尺寸发生改变,浏览器重新渲染部分或全部文档的过程就叫做回流。

会导致回流的操作:

  • 页面首次渲染
  • 浏览器窗口大小发生变化(window.resize发生变化)
  • 内容变化
  • 添加或删除节点
  • 激活CSS伪类
  • clientWidth(clientWidth = width+左右padding)、clientHeight(与clientHeight类似)、clientTop(border.top的宽度)、clientLeft(border.left宽度)、offsetWidth(width+左右padding+左右border)、offsetHeight(与offsetWidth类似)、offsetTop(当前元素上边框外边缘到最近的已定位父级(offsetParent)上边框内缘的距离,如果父级没有定位,则是到body顶部的距离)、offsetLeft(与offsetTop类似)发生改变

重绘repaint

当页面中元素样式的改变不影响它在文档流中的位置,浏览器会将新样式赋值给元素,这个过程叫做重绘

引起重绘的操作:

  • 设置background
  • 设置visibility
  • 设置color等

回流和重绘的性能影响

回流的性能消耗要比重绘大。

浏览器的优化机制

由于每次重排会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列中,知道过了一段时间或者操作达到了一个阈值,才会清空队列。当时,当获取布局信息时,会强制队列刷新,访问如下属性时会强制队列刷新:

  • offsetTopoffsetLeftoffsetWidthoffsetHeight
  • scrollTopscrollLeftscrollWidthscrollHeight
  • clientTopclientLeftclientWidthclientHeight
  • getComputedStyle(element,pseudoElement)(用于获取指定元素的CSS样式,element:必选,需要获取样式的元素;pseudoElement:可选,伪类元素,当不查询伪类元素时可以忽略或传入null)
  • getBoundingClientRect()(用于获取某个元素相对于视窗的位置集合。集合中有toprightbottom(top+元素高度),left等属性,可以用于实现吸顶操作)

应该避免使用上述属性或方法,如果要使用最好将值缓存起来

避免性能影响

CSS:

  • 避免使用table布局
  • 避免设置多层内联样式

JS:

1.合并多次DOM操作

//使用cssText
const el = element.getElementById('test')
el.style.cssText += 'border-left: 1px; border-right: 2px;padding: 5px'
//修改CSS的class
const el = document.getElementById('test')
el.className += 'active'

2.批量修改DOM,可以通过如下步骤减少回流重绘次数:

  1. 使元素脱离文档流
  2. 对其进行多次修改
  3. 将元素带回文档中

上述步骤中第一步和第三步可能会引起回流,但是进过第一步之后,对DOM的所有修改都不会引起回流重绘。

有三种方式可以让DOM脱离文档流:

  • 隐藏元素,应用修改,重新显示
  • 使用文档锁片(documentFragment)在当前DOM之外构建一个子树,在把它拷贝回文档
  • 将元素元素拷贝到一个脱离文档的节点中,修改节点后,再替换元素的元素

隐藏元素,应用修改,重新显示

在展示和隐藏节点时会产生两次回流:

function appendDataToElement(appendToElement,data) {
    let li
    for(let i = 0; i < data.length; i++) {
        li = document.createElement('li')
        li.textContent = 'text'
        appendToElement.appendChild(li)
    }
}
const ul = document.getElementById('list')
ul.style.display = 'none'
appendDataToElement(ul,data)
ul.style.display = 'block'

使用文档碎片(documentFragment)在当前DOM之外构建一个子树,再把它拷贝回文档

const ul = document.getElementById('list')
const fragment = document.createDocumentFragment()
appendDataToElement(fragment,data)
ul.appendChild(fragment)

将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始元素

const ul = document.getElementById('list')
const clone = ul.cloneNode(true)
appendDataToElement(clone,data)
ul.parentNode.replaceChild(clone,ul)

上述三种方法优化效果一般,不用优先考虑

避免触发同步布局事件

例如:

function initP() {
    for(let i = 0; i < paragraphs.length; i++) {
        paragraphs[i].style.width = box.offsetWidth + 'px'
    }
}

上述代码每次循环的时候都读取boxoffsetWidth属性值,每次循环都会强制浏览器刷新队列。可以优化为:

const width = box.offsetWidth
function initP() {
    for(let i = 0; i < paragraphs.length; i++) {
        paragraphs[i].style.width = width + 'px'
    }
}

对于复杂动画效果,使用绝对定位让其脱离文档流

对于复杂动画效果,由于经常引起回流重绘,我们可以使用绝对定位,让其脱离文档流。

CSS3硬件加速(GPU加速)

  1. 使用CSS3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘
  2. 对于动画的其他属性,比如background-color这些,还是会引起回流重绘,不过它还是可以提升这些动画的性能

常见触发硬件加速的css属性:

  • transform
  • opacity
  • filter
  • Will-change

CSS3硬件加速的坑

  • 如果太多元素使用css3硬件加速,会导致内存占用较大,会有性能问题
  • GPU渲染字体弧导致抗锯齿无效,这是因为GPUCPU的算法不同,如果不在动画结束时关闭硬件加速,会产生字体模糊

性:

  • transform
  • opacity
  • filter
  • Will-change

CSS3硬件加速的坑

  • 如果太多元素使用css3硬件加速,会导致内存占用较大,会有性能问题
  • GPU渲染字体弧导致抗锯齿无效,这是因为GPUCPU的算法不同,如果不在动画结束时关闭硬件加速,会产生字体模糊
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值