不少人都知道要减小浏览器的重排和重绘,但对其中的具体原理以及如何具体操做并非很了解,当忽然提起这个话题的时候,仍是会一脸懵逼。但愿你们能够耐着性子阅读本文,仔细琢磨,完全掌握这个知识点!css
第四步和第五步是最耗时的部分,这两步合起来,就是咱们一般所说的渲染。前端
网上找了一张图片,我加了注释会更直观一些:git
网页生成的时候,至少会渲染一次。github
在用户访问的过程当中,还会不断从新渲染web
从新渲染须要重复以前的第四步(从新生成布局)+第五步(从新绘制)或者只有第五个步(从新绘制)。面试
大,在这个语境里的意思是:谁能影响谁?浏览器
就如上面的概念同样,单单改变元素的外观,确定不会引发网页从新生成布局,但当浏览器完成重排以后,将会从新绘制受到这次重排影响的部分。缓存
好比改变元素高度,这个元素乃至周边dom都须要从新绘制。
复制代码
也就是说:"重绘"不必定会出现"重排","重排"必然会出现"重绘"性能优化
当DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器须要从新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫作重排。
重排也叫回流,重排的过程如下面这种理解方式更清晰一些:
回流就比如向河里(文档流)扔了一块石头(dom变化),激起涟漪,而后引发周边水流受到波及,因此叫作回流
任何会改变元素几何信息(元素的位置和尺寸大小)的操做,都会触发重排,下面列一些栗子:
常见引发重排属性和方法 | |||
---|---|---|---|
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
结构,尽量小的影响重排的范围。
而不是像全局范围的示例代码同样一溜的堆砌标签,随便一个元素触发重排都会致使全局范围的重排。
概念:
当一个元素的外观发生改变,但没有改变布局,从新把元素外观绘制出来的过程,叫作重绘。
常见的引发重绘的属性:
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样式请求:
咱们在开发中,应该谨慎的使用这些style请求,注意上下文关系,避免一行代码一个重排,这对性能是个巨大的消耗
就像上文提到的咱们要尽量的减小重排次数、重排范围,这样说很泛,下面是一些行之有效的建议,你们能够参考一下。
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,由于渲染队列原本就是空的,因此并无触发重排,仅仅拿值而已。
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;";
复制代码
// 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';
复制代码
隐藏要操做的dom
在要操做dom以前,经过display隐藏dom,当操做完成以后,才将元素的display属性为可见,由于不可见的元素不会触发重排和重绘。
dom.display = 'none'
// 修改dom样式
dom.display = 'block'
复制代码
经过使用DocumentFragment建立一个dom
碎片,在它上面批量操做dom,操做完成以后,再添加到文档中,这样只会触发一次重排。
复制节点,在副本上工做,而后替换它!
position属性为absolute或fixed的元素,重排开销比较小,不用考虑它对其余元素的影响
能够把动画效果应用到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脚本执行效率低的关键因素之一,重排与重绘做为大厂常常出现的面试题,而且涉及的性能优化,这是前端必须掌握的基本概念/技能之一(敲黑板!)。
重排会不断触发这是不可避免的,但咱们在开发时,应尽可能按照文中的建议来组织代码,这种优化,须要平时有意识的去作,一点一滴的去作,但愿你们重视一下。
以上2018.12.17
参考资料: