回流:
构造完渲染树后,会将DOM节点与对应的CSS结合,同时我们要进入布局阶段,还需要计算他们在设备视口ViewPort内的确切位置的大小,这个阶段就是回流
谷歌开发者文档
重绘:当DOM变化影响元素的几何属性(宽高边距等),浏览器会重新绘制受影响的部分属性到屏幕中,这个过程叫 “重绘”.
JavaScript高性能
浏览器下载完页面中的所有组件-HTML,JS,CSS,图片,之后会生成两个内部数据结构:
DOM树:表示页面结构
渲染树:表示DOM节点如何展示
DOM树中的每个需要现实的节点在渲染树中至少存在一个对应的节点。
渲染树中的节点被称为 “帧” 或者 “盒” ,一旦DOM盒渲染树构建完成,浏览器就开始绘制页面元素。
当DOM变化影响的元素的几何属性(宽和高)-比如改变边框宽度导致行数增加-浏览器需要重新计算元素的几何属性。浏览器会使渲染树中收到影响的部分失效,并重新构造渲染树,这个过程称为 “重排(reflow)”
完成重排后,浏览器会重新绘制受影响的部分到屏幕中,称为“重绘(repaint)”,并不是所有的DOM变化会影响元素的几何属性。
重排何时发生:
最小化重绘和重排:
- 延迟访问布局信息:合并多次改变,一次修改class而不是内联
- 适当使用display
- 创建文档片段:遍历和重排最少
- 创建节点备份
- 缓存布局信息
- 脱标
- 减少使用:hover伪选择器
- 事件委托
什么影响布局/回流?
以下所有属性或方法,当在 JavaScript 中请求/调用时,将触发浏览器同步计算样式和布局*。这也称为回流或布局抖动,是常见的性能瓶颈。
一般所有同步提供布局指标的API都会触发强制回流/布局。继续阅读以了解更多案例和详细信息。
元素 API
获取盒子指标
elem.offsetLeft
,elem.offsetTop
,elem.offsetWidth
,elem.offsetHeight
,elem.offsetParent
elem.clientLeft
,elem.clientTop
,elem.clientWidth
,elem.clientHeight
elem.getClientRects()
,elem.getBoundingClientRect()
滚动的东西
elem.scrollBy()
,elem.scrollTo()
elem.scrollIntoView()
,elem.scrollIntoViewIfNeeded()
elem.scrollWidth
,elem.scrollHeight
elem.scrollLeft
,elem.scrollTop
也, 设置它们
设置焦点
elem.focus()
(来源)
还…
elem.computedRole
,elem.computedName
elem.innerText
(来源)
获取窗口尺寸
window.scrollX
,window.scrollY
window.innerHeight
,window.innerWidth
- window.visualViewport.height/width/offsetTop/offsetLeft(源码)
文档
document.scrollingElement
只强制风格document.elementFromPoint
形式:设置选择+焦点
inputElem.focus()
inputElem.select()
,textareaElem.select()
鼠标事件:读取偏移数据
mouseEvt.layerX
,mouseEvt.layerY
,mouseEvt.offsetX
,mouseEvt.offsetY
(来源)
调用 getComputedStyle()
window.getComputedStyle()
通常会强制重新计算样式。
window.getComputedStyle()
通常也会强制布局。
gCS() 强制布局条件的详细信息
获取Range
维度
range.getClientRects()
,range.getBoundingClientRect()
SVG
相当多的属性/方法强制,但我没有列出详尽的清单。此列表不完整:
- SVGLocatable:
computeCTM()
,getBBox()
- SVGTextContent:
getCharNumAtPosition()
,getComputedTextLength()
,getEndPositionOfChar()
,getExtentOfChar()
,getNumberOfChars()
,getRotationOfChar()
,getStartPositionOfChar()
,getSubStringLength()
,selectSubString()
- SVG使用:
instanceRoot
使用下面的“铬源树链接”自行探索!
内容可编辑
- 很多很多东西,…包括将图像复制到剪贴板(来源)
附录
仅当文档已更改并使样式或布局无效时,重排才会产生成本。通常,这是因为 DOM 已更改(修改了类,添加/删除了节点,甚至添加了 :focus 之类的伪类)。
如果强制布局,则必须先重新计算样式。所以强制布局会触发这两种操作。它们的成本很大程度上取决于内容/情况,但通常两种操作的成本相似。
你应该怎么做?好吧,
More on forced layout
下面的部分更详细地介绍了所有内容,但简短的版本是:
for
强制布局和更改 DOM 的循环是最糟糕的,避免它们。- 使用 DevTools 性能面板查看发生这种情况的位置。您可能会惊讶地看到您的应用程序代码和库代码出现这种情况的频率。
- 批量写入和读取 DOM(通过FastDOM或虚拟 DOM 实现)。在帧的开头(非常开始
rAF
,滚动处理程序等)阅读您的指标,当数字仍然与上次布局完成时相同。
卫报的时间线痕迹。Outbrain 正在反复强制布局,可能是在一个循环中。
跨浏览器
- 以上数据是通过阅读 Blink 源码构建的,因此对于 Chrome、Opera、Brave、Edge 和大多数安卓浏览器都是如此。您可以在 Chromium 源代码树中自行浏览它们。
- Tony Gentilcore 的 Layout Triggering List是针对 2011 WebKit 的,通常与上述一致。
- 现代 WebKit 的强制布局实例大多是一致的:
updateLayoutIgnorePendingStylesheets
- GitHub 搜索 - WebKit/WebKit - Gecko 的回流似乎是通过 FrameNeedsReflow 请求的。结果:
FrameNeedsReflow
- Mozilla-central searchfox - 没有关于 IE 或 EdgeHTML 的具体数据,但它们可能大致相同,因为这些属性的返回值是指定的。
更多关于强制布局
- 避免布局抖动 — Web 基础知识识别和修复此主题的最佳资源。
- CSS 触发器- 涵盖了设置/更改给定 CSS 值所需的操作。然而,上面的列表都是关于从 JavaScript 同步强制紫色/绿色/深绿色圆圈的原因。
- 修复现实世界中的布局抖动| 马特·安德鲁斯
- 时间轴演示:诊断强制同步布局 - Google Chrome
- 防止“布局颠簸” | 威尔逊页
- 威尔逊佩奇/fastdom
- 渲染:重绘、回流/重新布局、重新设计 / Stoyan
- 我们花了一周时间让 Trello 看板加载速度非常快。这是我们如何做到的。- 雾溪博客
- 最小化浏览器回流 | PageSpeed 见解 | 谷歌开发者
- 在 iOS 上优化 UIWebViews 和网站中的 Web 内容
- Chrome 中的加速渲染
- 好奇的网络性能
- 免费
参考
JavaScript高性能
MDN