浏览器重绘(repaint)重排(reflow)与优化[浏览器机制]

不少人都知道要减小浏览器的重排和重绘,但对其中的具体原理以及如何具体操做并非很了解,当忽然提起这个话题的时候,仍是会一脸懵逼。但愿你们能够耐着性子阅读本文,仔细琢磨,完全掌握这个知识点!css

博客前端积累文档公众号GitHubhtml

网页生成过程:

  1. HTML被HTML解析器解析成DOM 树
  2. css则被css解析器解析成CSSOM 树
  3. 结合DOM树和CSSOM树,生成一棵渲染树(Render Tree)
  4. 生成布局(flow),即将全部渲染树的全部节点进行平面合成
  5. 将布局绘制(paint)在屏幕上

第四步和第五步是最耗时的部分,这两步合起来,就是咱们一般所说的渲染前端

网上找了一张图片,我加了注释会更直观一些:git


渲染:

网页生成的时候,至少会渲染一次github

在用户访问的过程当中,还会不断从新渲染web

从新渲染须要重复以前的第四步(从新生成布局)+第五步(从新绘制)或者只有第五个步(从新绘制)。面试

重排比重绘大:

大,在这个语境里的意思是:谁能影响谁?浏览器

  • 重绘:某些元素的外观被改变,例如:元素的填充颜色
  • 重排:从新生成布局,从新排列元素。

就如上面的概念同样,单单改变元素的外观,确定不会引发网页从新生成布局,但当浏览器完成重排以后,将会从新绘制受到这次重排影响的部分缓存

好比改变元素高度,这个元素乃至周边dom都须要从新绘制。
复制代码

也就是说:"重绘"不必定会出现"重排","重排"必然会出现"重绘"性能优化

重排(reflow):

概念:

当DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器须要从新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫作重排。

重排也叫回流,重排的过程如下面这种理解方式更清晰一些:

回流就比如向河里(文档流)扔了一块石头(dom变化),激起涟漪,而后引发周边水流受到波及,因此叫作回流

常见引发重排属性和方法

任何会改变元素几何信息(元素的位置和尺寸大小)的操做,都会触发重排,下面列一些栗子:

  1. 添加或者删除可见的DOM元素;
  2. 元素尺寸改变——边距、填充、边框、宽度和高度
  3. 内容变化,好比用户在input框中输入文字
  4. 浏览器窗口尺寸改变——resize事件发生时
  5. 计算 offsetWidth 和 offsetHeight 属性
  6. 设置 style 属性的值
常见引发重排属性和方法
width height margin padding
display border position overflow
clientWidth clientHeight clientTop clientLeft
offsetWidth offsetHeight offsetTop offsetLeft
scrollWidth scrollHeight scrollTop scrollLeft
scrollIntoView() scrollTo() getComputedStyle()
getBoundingClientRect() scrollIntoViewIfNeeded()

重排影响的范围:

因为浏览器渲染界面是基于流失布局模型的,因此触发重排时会对周围DOM从新排列,影响的范围有两种:

  • 全局范围:从根节点html开始对整个渲染树进行从新布局。
  • 局部范围:对渲染树的某部分或某一个渲染对象进行从新布局

全局范围重排

<body>
  <div class="hello">
    <h4>hello</h4>
    <p><strong>Name:</strong>BDing</p>
    <h5>male</h5>
    <ol>
      <li>coding</li>
      <li>loving</li>
    </ol>
  </div>
</body>
复制代码

当p节点上发生reflow时,hello和body也会从新渲染,甚至h5和ol都会收到影响。

局部范围重排:

用局部布局来解释这种现象:把一个dom的宽高之类的几何信息定死,而后在dom内部触发重排,就只会从新渲染该dom内部的元素,而不会影响到外界。

尽量的减小重排的次数、重排范围:

重排须要更新渲染树,性能花销很是大:

它们的代价是高昂的,会破坏用户体验,而且让UI展现很是迟缓,咱们须要尽量的减小触发重排的次数。

重排的性能花销跟渲染树有多少节点须要从新构建有关系:

因此咱们应该尽可能以局部布局的形式组织html结构,尽量小的影响重排的范围。

而不是像全局范围的示例代码同样一溜的堆砌标签,随便一个元素触发重排都会致使全局范围的重排。

重绘(repaint):

概念

当一个元素的外观发生改变,但没有改变布局,从新把元素外观绘制出来的过程,叫作重绘。

常见的引发重绘的属性:

color border-style visibility background
text-decoration background-image background-position background-repeat
outline-color outline outline-style border-radius
outline-width box-shadow background-size

浏览器的渲染队列:

思考如下代码将会触发几回渲染?

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
复制代码

根据咱们上文的定义,这段代码理论上会触发4次重排+重绘,由于每一次都改变了元素的几何属性,实际上最后只触发了一次重排,这都得益于浏览器的渲染队列机制

当咱们修改了元素的几何属性,致使浏览器触发重排或重绘时。它会把该操做放进渲染队列,等到队列中的操做到了必定的数量或者到了必定的时间间隔时,浏览器就会批量执行这些操做。

强制刷新队列:

div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);
复制代码

这段代码会触发4次重排+重绘,由于在console中你请求的这几个样式信息,不管什么时候浏览器都会当即执行渲染队列的任务,即便该值与你操做中修改的值没关联。

由于队列中,可能会有影响到这些值的操做,为了给咱们最精确的值,浏览器会当即重排+重绘

强制刷新队列的style样式请求

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop, scrollLeft, scrollWidth, scrollHeight
  3. clientTop, clientLeft, clientWidth, clientHeight
  4. getComputedStyle(), 或者 IE的 currentStyle

咱们在开发中,应该谨慎的使用这些style请求,注意上下文关系,避免一行代码一个重排,这对性能是个巨大的消耗

重排优化建议

就像上文提到的咱们要尽量的减小重排次数、重排范围,这样说很泛,下面是一些行之有效的建议,你们能够参考一下。

1. 分离读写操做

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);
复制代码

仍是上面触发4次重排+重绘的代码,此次只触发了一次重排:

在第一个console的时候,浏览器把以前上面四个写操做的渲染队列都给清空了。剩下的console,由于渲染队列原本就是空的,因此并无触发重排,仅仅拿值而已。

2. 样式集中改变

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
复制代码

虽然如今大部分浏览器有渲染队列优化,不排除有些浏览器以及老版本的浏览器效率仍然低下:

建议经过改变class或者csstext属性集中改变样式

// bad
var left = 10;
var 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;";
复制代码

3. 缓存布局信息

// bad 强制刷新 触发两次重排
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';

// good 缓存布局信息 至关于读写分离
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
复制代码

4. 离线改变dom

  • 隐藏要操做的dom

    在要操做dom以前,经过display隐藏dom,当操做完成以后,才将元素的display属性为可见,由于不可见的元素不会触发重排和重绘。

    dom.display = 'none'
    // 修改dom样式
    dom.display = 'block'
    复制代码
  • 经过使用DocumentFragment建立一个dom碎片,在它上面批量操做dom,操做完成以后,再添加到文档中,这样只会触发一次重排。

  • 复制节点,在副本上工做,而后替换它!

5. position属性为absolute或fixed

position属性为absolute或fixed的元素,重排开销比较小,不用考虑它对其余元素的影响

6. 优化动画

  • 能够把动画效果应用到position属性为absolute或fixed的元素上,这样对其余元素影响较小

    动画效果还应牺牲一些平滑,来换取速度,这中间的度本身衡量:

    好比实现一个动画,以1个像素为单位移动这样最平滑,可是reflow就会过于频繁,大量消耗CPU资源,若是以3个像素为单位移动则会好不少。

  • 启用GPPU加速

    此部分来自优化CSS重排重绘与浏览器性能

    GPU(图像加速器):

    GPU 硬件加速是指应用 GPU 的图形性能对浏览器中的一些图形操做交给 GPU 来完成,由于 GPU 是专门为处理图形而设计,因此它在速度和能耗上更有效率。

    GPU 加速一般包括如下几个部分:Canvas2D,布局合成, CSS3转换(transitions),CSS3 3D变换(transforms),WebGL和视频(video)。

    /* * 根据上面的结论 * 将 2d transform 换成 3d * 就能够强制开启 GPU 加速 * 提升动画性能 */
    div {
      transform: translate3d(10px, 10px, 0);
    }
    复制代码

结语

重排也是致使DOM脚本执行效率低的关键因素之一,重排与重绘做为大厂常常出现的面试题,而且涉及的性能优化,这是前端必须掌握的基本概念/技能之一(敲黑板!)。

重排会不断触发这是不可避免的,但咱们在开发时,应尽可能按照文中的建议来组织代码,这种优化,须要平时有意识的去作,一点一滴的去作,但愿你们重视一下。

但愿看完的朋友能够点个喜欢/关注,您的支持是对我最大的鼓励。

博客前端积累文档公众号GitHub

以上2018.12.17

参考资料:

网页性能管理详解

优化CSS重排重绘与浏览器性能

相关文章
相关标签/搜索