DOM树
表示页面结构
渲染树
表示DOM节点如何展示
DOM树中需要展示的节点在渲染树中至少存在一个对应的节点(隐藏的DOM元素在渲染树中没有对应的节点)。渲染树中的节点被称为“帧(frames)”或“盒(boxes)”。符合CSS模型的定义。理解页面元素为一个具有内边距、外边距、边框、位置的盒子。一旦DOM和渲染树构建完成,浏览器就开始显示(绘制 paint)页面元素。
当DOM元素变化影响了元素的集合属性(宽和高)——比如改变边框宽度或者给段落添加文字,导致行数增加——浏览器需要重新计算元素的几何属性, 同样其他元素的几何属性和位置也会因此受到影响。 浏览器会使渲染树中受到影响的部分失效, 并重新构造渲染树。 这个过程称为 “ 重排(reflow)”。完成重排后, 阅览器会重新绘制受影响的部分到屏幕中,该过程称为 “ 重绘(repaint)"。
并不是所有的DOM变化都会影响几何属性。 例如, 改变一个元素的背景色并不会影响色的宽和高。 在这种情况下, 只会发生一次重绘(不需要重排), 因为元素的布局并没有改变。
重绘和重排操作都是代价昂贵的操作,它们会导致 Web 应用程序的 UI 反应迟钝。所以, 应当尽可能减少这类过程的发生。
重排何时发生
正如前文所提到的, 当页面布局和几何属性改变时就需要“重排”。下述情况中会发生重排。
- 添加或删除可见的DOM元素。
- 元素位置改变。
- 元素尺寸改变(包括:夕|、边距、 内边距、 边框厚度、 宽度、 高度等属性改变)。内容改变, 例如: 文本改变或图片被另一个不同尺寸的图片替代。
- 页面谊染器初始化。
- 浏览器窗口尺寸改变。
根据改变的范围和程度, 渲染树中或大或小的对应的部分也需要重新计算。 有些改变会触发整个页面的重排: 例如, 当滚动条出现时。
渲染树变化的排队与刷新
由干每次重排都会产生计算消耗, 大多数浏览器通过队列化修改并批量执行来优化重排过程。 然而, 你可能会(经常不知不觉)强制刷新队列并要求计划任务立刻执行。 获取布局信息的操作会导致歹lj队刷新, 比如以下方法:
- offsetTop, offsetleft, offsetWidth, offsetHeight
- scrollTop, scrollleft, scrollWidth, scrollHeight
- clientTop, clientleft, clientWidth, clientHeight
- getComputedStyle() (currentStyle in IE)
以上属性和方在是需要返回最新的布局信息, 因此浏览器不得不执行渲染列队中的 “待处理变化” 井触发重排以返回正确的值。
在修改样式的过程中, 最好避免使用上面列出的属性。它们都会刷新渲染队列, 即使你是在获取最近未发生改变的或者与最新改变无关的布局信息。
最小化重绘和重排
重绘和重排可能代价非常昂贵,因此一个好的提高程序响应速度的策略就是减少此类操作的发生。为了减少发生次数,应该合并多次对DOM和样式的修改,然后一次处理掉。
改变样式
考虑这个例子:
var el= document.getElementByid( 'mydiv');
el.style.borderleft = 'lpx'
el. style.borderRight = '2px '
el. style.padding = '5px'
示例中有三个样式属性被改变,每一 个都会影响元素的几何结构。 最糟糕的情况下,会导致浏览器触发三次重排。 大部分现代浏览器为此做了优化, 只会触发一次重排, 但是在旧版浏览器中或者有一个分离的异步处理过程时(比如使用计时器), 仍然效率低下。 如果在 上面代码执行时, 有其他代码请求布局信息, 这会导致触发三次重排。 而且, 这段代码四 次访问DOM, 可以被优化。
够达到同样效果且效率更高的方式是: 合并所有的改变然后一次处理, 这样只会修改DOM一次。 使用cssText属性可以实现:
el.style.cssText += ' ;border-left: 1px' ;
另一个一次性修改样式的办法是修改 css的class名称, 而不是修改内联样式。 这种方法适合那些不依赖于运行逻辑和计算的情况。 改变 css 的class名称的方也更清晰, 更易于维护。它有助于保持你的脚本与免除显示性代码, 尽管它可能带来轻微的性能影响, 因为 改变类时需要检查级联样式。
var el= document.getElementByld('mydiv');
el.className ='active'
批量修改DOM
当需要对DOM元素进行一系列操作时, 可以通过以下步骤来减少重绘和重排的次数:
- 使元素脱离文档流。
- 对其应用多重改变。
- 把元素带回文档中。
该过程里会触发两次重排一一一第一步和第三步。 如果你忽略这两个步骤, 那么在第二步所产生的任何修改都会触发一次重排。
有三种基本方法可以使DOM脱离文档:
- 隐藏元素, 应用修改, 重新显示。
- 使用文档片断(docuement fragment)在当前DOM之外构建一个子树, 再把它拷贝回文档。
- 将原始元素拷贝到一个脱离文档的节点中,改副本, 完成后再替换原始元素。
缓存布局信息
如前文所述,浏览器尝试通过队列化修改和批量执行的方式最小化重排次数。当你查询布局信息时,比如获取偏移量(offsets)、滚动位置(scroll values)或计算出的样式值(computedsytle values)时,浏览器为了返回最新值,会刷新队列并应用所有变更。最好的做法是尽量减少布局信息的获取次数,获取后把它赋值给局部变量,然后再操作局部变量。
考虑一个例子,把myElement元素沿对角线移动,每次移动一个像素,从100像素×100像素的位置开始,到500像素x500像素的位置结束。在timeout循环体中你可以使用下面的方法:
//低效的
myElement.style.left = 1 + myElement.offsetLeft +'px'
myElement.style.top = 1 + myElement.offsetTop +'px'
if (myElement.offsetLeft >= 500) {
stop()
}
这种方法效率低下,因为元素每次移动时都会查询偏移量,导致浏览器刷新渲染列队而不利于优化。一个更好的方法是,获取一次起始位置的值,然后将其赋值给一个变量,比如var current = myElement.offsetleft。然后,在动画循环中,直接使用current变量而不再用偏移量:
current++
myElement.style.left = current+'px'
myElement.style.top = current+'px'
if (current >= 500) {
stop()
}
让元素脱离动画流
用展开/折叠的方式来显示和隐藏部分页面是一种常见的交互模式。它通常包括展开区域的几何动画,井将页面其他部分推向下方。
一般来说,重排只影响渲染树中的一小部分,但也可能影响很大的部分,甚至整个渲染树。 浏览器所需要重排的次数越少,应用程序的响应速度就越快。因此当页面顶部的一个动画推移页面整个余下的部分时,会导致一次代价昂贵的大规模重排,让用户感到页面一顿一 顿的。渲染树中需要重新计算的节点越多,情况就会越糟。
使用以下步嘱可以避免页面中的大部分重排:
- 使用绝对位置定位页面上的动画元素, 将其脱离文档流。
- 让元素动起来。 当它扩大时, 会的时覆盖部分页面。 但只是页面一个区域的重绘过程, 不会产生重排并重绘页面的大部分内容。
- 当动画结束时恢复定位, 从而只会下移一次文档的其他元素。
IE和:hover
从IE 7开始,IE允许在任何元素(严格模式下)上使用:hover这个 css 伪选择器。 然而,如果你有大量元素使用了:hover, 那么会降低响应速度。 此问题在IE8中更为明显。文章来源:https://www.toymoban.com/news/detail-846551.html
例如,如果你创建一个5列和500~1000行的表格,并使用tr:hover改变背景色来高亮显示鼠标所在的当前行, 当鼠标在表格上移动时, 性能会降低。 高亮过程会变慢, CPU使用率会提高到80%~90%。所以在元素很多时应避免使用这种效果,比如很大的表格或很长的列表。文章来源地址https://www.toymoban.com/news/detail-846551.html
到了这里,关于【DOM】重绘与重排详解及在性能优化中的应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!