前端性能-回流&重绘

回流&重绘

先了解浏览器渲染过程

参考自 (MDN).javascript

浏览器渲染大体流程:

  1. 处理 HTML 标记并构造 DOM 树。
  2. 处理 CSS 标记并构造 CSSOM 树。
  3. 将 DOM 与 CSSOM 合并成一个渲染树。
  4. 根据渲染树来布局,以计算每一个节点的几何信息。
  5. 将各个几点绘制到屏幕上。

生成渲染树:

构建渲染树,浏览器须要作一下工做:java

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

第一步遍历节点的时候,须要知道什么节点是不可见的。web

  • 一些不会渲染输出的节点。好比 script、meta、link等。
  • 某些节点经过 CSS 隐藏。注意 visibility: hidden 与 display: none 是不同的。前者隐藏元素,但元素仍占据着布局空间(即将其渲染成一个空框),然后者 (display: none) 将元素从渲染树中彻底移除,元素既不可见,也不是布局的组成部分。

注意: 渲染树只包含可见的节点浏览器

回流(reflow)

Render Tree 中部分或所有, 因元素的尺寸、布局、隐藏等改变而须要从新构建,浏览器从新渲染的过程称为 回流。缓存

会致使回流的操做:函数

  • 页面首次渲染。
  • 浏览器窗口大小发生改变。
  • 元素尺寸或者位置发生改变。
  • 元素内容变化(文字数量或者图片大小发生改变)。
  • 元素字体大小的改变。
  • 添加或者删除可见DOM 元素。
  • 激活 CSS 伪类 (eg: :hover)。
  • 查询某些属性或调用某些方法。

一些经常使用且会致使回流的属性和方法。布局

  • clientWidthclientHeightclientTopclientLeft
  • offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

重绘(repaint)

当页面中元素样式的改变并不影响b布局时(eg:colorbackground-color等),浏览器会将新样式赋予给元素并从新绘制它,这个过程称为重绘。性能

一句话: 回流必将引发重绘,重绘不必定会引发回流。字体

性能影响

有时即便仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。优化

现代浏览器会对频繁的回流或重绘操做进行优化:

浏览器会维护一个队列,把全部引发回流和重绘的操做放入队列中,若是队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样能够把屡次回流和重绘变成一次。

当你访问如下属性或方法时,浏览器会马上清空队列:

  • clientWidthclientHeightclientTopclientLeft
  • offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

由于队列中可能会有影响到这些属性或方法返回值的操做,即便你但愿获取的信息与队列中操做引起的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的。

优化重绘回流

  • 减小对 render tree 的操做 【合并屡次多DOM和样式的修改】
  • 减小对一些style信息的请求,尽可能利用好浏览器的优化策略

CSS

  1. 直接改变className. (把要修改的样式集中到一个 class 内统一修改);
  2. 避免使用 table 布局 (尽可能不要使用表格布局,若是没有定宽表格一列的宽度由最宽的一列决定,那么极可能在最后一行的宽度超出以前的列宽,引发总体回流形成table可能须要屡次计算才能肯定好其在渲染树中节点的属性,一般要花3倍于同等元素的时间。)
  3. 尽量在DOM树的最末端改变class,尽量在DOM树的里面改变class(能够限制回流的范围)
  4. 将须要屡次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其余元素。例若有动画效果的元素就最好设置为绝对定位;
  5. 使用display:none技术,只引起两次回流和重绘;

JS

  1. 避免频繁操做样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。(而不是利用js控制样式)
  2. 不要常常访问会引发浏览器缓存队列的属性(上述那些浏览器会马上清空队列的属性)。若是确实要访问,利用缓存。eg:
// bad
for (var i = 0; i < len; i++) {
  el.style.left = el.offsetLeft + x + "px";
  el.style.top = el.offsetTop + y + "px";
}
// good
var x = el.offsetLeft,
    y = el.offsetTop;
for (var i = 0; i < len; i++) {
  x += 10;
  y += 10;
  el.style = x + "px";
  el.style = y + "px";
}

复制代码
  1. 尽可能将须要改变DOM的操做一次完成
let box = document.getElementById("box").style;
// bad
box.color = "red";    // 重绘
box.size = "14px";    // 回流、重绘
// good
box.bord = '1px solid red'
复制代码
  1. 对具备复杂动画的元素使用绝对定位,使它脱离文档流,不然会引发父元素及后续元素频繁回流。
  2. 避免频繁操做 DOM,建立一个documentFragment,在它上面应用全部 DOM 操做,最后再把它添加到文档中。

DocumentFragment,文档片断接口,表示一个没有父级文件的最小文档对象. 与 Document 最大的区别是由于 DocumentFragment 不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的(从新渲染) ,且不会致使性能等问题。

可使用 document.createDocumentFragment 方法或者构造函数来建立一个空的 DocumentFragment.

相关文章
相关标签/搜索