了解浏览器中的重排和重绘
重喷和重流
导致重流和重绘的原因是什么?
最大限度减少重绘和重排
Chrome 工具性能
资源:
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
最近我了解到了重排(Reflow)和重绘(Repaint)技术,以及它们对网页性能的影响。我写这篇文章就是为了深入探讨重排和重绘。
在正式开始之前,我们先来了解一下浏览器是如何渲染网站的。
- 当用户输入网址时,它会从服务器获取HTML源代码。
- 浏览器解析HTML源代码并将其转换为Token。
<, TagName, Attribute, AttributeValue, > - 令牌将转换为节点,并构建DOM 树。
- CSSOM树将根据 CSS 规则生成。
- DOM 树和 CSSOM 树将合并成渲染树。
- 渲染树的构造方式如下:
- 从 DOM 树的根节点开始,计算哪些元素可见及其计算样式。
- RenderTree 将忽略不可见的元素,例如
(meta, script, link)和display:none - 它会将可见节点与相应的 CSSOM 规则进行匹配并应用这些规则。
- 重排:计算每个可见节点的位置和大小
- 重绘:现在,浏览器会在屏幕上绘制渲染树。
重喷和重流
- 当改变元素的可见性,但不影响布局时,就会发生重绘。
- 例如:可见性、背景颜色、轮廓
- 重排是指重新计算文档中元素的位置和几何形状。当元素发生更改,影响到部分或整个页面的布局时,就会发生重排。元素的重排会导致 DOM 中所有子元素和父元素的后续重排。
回流焊和重喷都是成本很高的操作。
根据 Opera 的说法,大多数页面重排本质上都会导致页面重新渲染:
页面重排会严重影响性能,是导致 DOM 脚本运行缓慢的主要原因之一,尤其是在
处理能力较低的设备(例如手机)上。在很多情况下,页面重排相当于重新布局整个页面。
这是重排可视化的视频。
导致重流和重绘的原因是什么?
- 添加、删除或更新 DOM 节点时会发生重排。
- 使用隐藏 DOM 元素
display: none会导致页面重排和页面重绘。 - 隐藏 DOM 元素
visibility: hidden只会导致页面重绘,因为布局和位置不会发生变化。 - 移动或动画化 DOM 节点会触发重排和重绘。
- 调整窗口大小会触发重排。
- 更改字体样式会改变元素的几何形状。这意味着它可能会影响页面上其他元素的位置或大小,而这两种情况都需要浏览器执行重排。一旦这些布局操作完成,任何受损的像素都需要重新绘制。
- 添加或移除样式表会导致页面重排/重绘。
- 脚本操作 DOM 是一项开销很大的操作,因为每次文档或文档的某个部分发生更改时,脚本都需要重新计算。正如我们从触发页面重排的各种因素中看到的那样,页面重排每秒可能发生成千上万次。
var bodyStyle = document.body.style; // cache
bodyStyle.padding = "20px"; // reflow, repaint
bodyStyle.border = "10px solid red"; // reflow, repaint
bodyStyle.color = "blue"; // repaint only, no dimensions changed
bstyle.backgroundColor = "#cc0000"; // repaint
bodyStyle.fontSize = "2em"; // reflow, repaint
// new DOM element - reflow, repaint
document.body.appendChild(document.createTextNode('Hello!'));
最大限度减少重绘和重排
为了减少页面重排/重绘对用户体验的负面影响,策略很简单,就是减少页面重排和重绘的次数,以及对样式信息的请求,这样浏览器就能优化页面重排。那么,该如何实现呢?
- 不要逐个修改样式。为了保持代码的简洁性和可维护性,最好修改类名,而不是样式本身。如果样式是动态的,请编辑cssText属性。
// bad
var left = 10,
top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// better
el.className += " theclassname";
// or when top and left are calculated dynamically...
// better
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
-
批量 DOM 更改
- 使用容器
documentFragment来保持温度变化 - 克隆、更新、替换节点
- 隐藏元素
display: none(1 次重排,1 次重绘),添加 100 次更改,恢复显示(总共 2 次重排,2 次重绘)
- 使用容器
-
不要重复请求计算样式,将它们缓存到变量中。
- 多次读/写操作(例如对元素的高度属性进行读/写)
- 多次向 DOM 写入数据,然后再读取数据,导致文档重排。
- 读取(缓存),写入(使布局失效),读取(触发布局)。
- 解决方法:先阅读所有内容,然后再写入所有内容。
Chrome 工具性能
Chrome 提供了一个很棒的工具,可以帮助我们了解代码的运行情况,例如有多少次重排(布局)和重绘,以及有关内存、事件等的更多详细信息。
错误代码
var box1Height = document.getElementById('box1').clientHeight;
document.getElementById('box1').style.height = box1Height + 10 + 'px';
var box2Height = document.getElementById('box2').clientHeight;
document.getElementById('box2').style.height = box2Height + 10 + 'px';
var box3Height = document.getElementById('box3').clientHeight;
document.getElementById('box3').style.height = box3Height + 10 + 'px';
var box4Height = document.getElementById('box4').clientHeight;
document.getElementById('box4').style.height = box4Height + 10 + 'px';
var box5Height = document.getElementById('box5').clientHeight;
document.getElementById('box5').style.height = box5Height + 10 + 'px';
var box6Height = document.getElementById('box6').clientHeight;
document.getElementById('box6').style.height = box6Height + 10 + 'px';
优化代码
var box1Height = document.getElementById('box1').clientHeight;
var box2Height = document.getElementById('box2').clientHeight;
var box3Height = document.getElementById('box3').clientHeight;
var box4Height = document.getElementById('box4').clientHeight;
var box5Height = document.getElementById('box5').clientHeight;
var box6Height = document.getElementById('box6').clientHeight;
document.getElementById('box1').style.height = box1Height + 10 + 'px';
document.getElementById('box2').style.height = box2Height + 10 + 'px';
document.getElementById('box3').style.height = box3Height + 10 + 'px';
document.getElementById('box4').style.height = box4Height + 10 + 'px';
document.getElementById('box5').style.height = box5Height + 10 + 'px';
document.getElementById('box6').style.height = box6Height + 10 + 'px';
资源:
- https://gist.github.com/faressoft/36cdd64faae21ed22948b458e6bf04d5
- https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
- http://www.stubbornella.org/content/2009/03/27/reflows-repaints-css-performance-making-your-javascript-slow/
- CSS触发器
- 什么因素导致布局/重排?
