什么导致强制布局/重排
Godiswill opened this issue · comments
什么导致强制布局/重排
原文链接
英文原文:What forces layout / reflow
以下所有属性或方法,当在 JS 中读写或调用,都将触发浏览器同步计算样式或布局。也被叫做重排或布局抖动,这通常是页面性能瓶颈。
元素
盒模型
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()
会造成两次强制布局 (source&l=2923)
其他
elem.computedRole
,elem.computedName
elem.innerText
(source&l=3440))
getComputedStyle
window.getComputedStyle()
典型的会造成强制计算样式
window.getComputedStyle()
以下情况将会导致强制布局:
- 该元素是影子树(shadow tree)
- 媒体查询(视口相关),尤其是以下情形 : (source)
min-width
,min-height
,max-width
,max-height
,width
,height
aspect-ratio
,min-aspect-ratio
,max-aspect-ratio
device-pixel-ratio
,resolution
,orientation
,min-device-pixel-ratio
,max-device-pixel-ratio
- 读取以下属性 : (source)
height
,width
top
,right
,bottom
,left
margin
[-top
,-right
,-bottom
,-left
, 或简写] 仅当 margin 是固定值.padding
[-top
,-right
,-bottom
,-left
,或简写] 仅当 padding 是固定值.transform
,transform-origin
,perspective-origin
translate
,rotate
,scale
grid
,grid-template
,grid-template-columns
,grid-template-rows
perspective-origin
- 以下这些貌似没影响了(自2018.02):
motion-path
,motion-offset
,motion-rotation
,x
,y
,rx
,ry
window
window.scrollX
,window.scrollY
window.innerHeight
,window.innerWidth
window.getMatchedCSSRules()
only forces style
Forms
inputElem.focus()
inputElem.select()
,textareaElem.select()
Mouse events
mouseEvt.layerX
,mouseEvt.layerY
,mouseEvt.offsetX
,mouseEvt.offsetY
(source)
document
doc.scrollingElement
仅强制计算样式
Range
range.getClientRects()
,range.getBoundingClientRect()
SVG
- 太多了,无法弄哦详尽的列表 , Tony Gentilcore's 2011 Layout Triggering List 列出了一些。
内容可编辑 contenteditable
- 太多了,包括复制图片到剪贴板 (source)
附录 *Appendix
- 重排的开销产生是由于文档改变导致先前的样式布局无效,典型的像 DOM 元素样式修改、添加删除,甚至添加一些伪类(如 :focus )都会导致重排。
- 在强制布局之前先要执行样式计算,所有强制布局需要执行渲染管道中的两者。样式计算和布局的开销取决于内容的复杂情况,一般两者的开销相比是差不多的。
- 最佳实践。关于更多强制布局的各方面细节可以在文末文章引用部分查看。以下是简要概括:
- 避免在 for 循环中同时进行会引发布局的操作和操作 DOM。
- 利用 Chrome 的 Performance 功能查看哪些代码或第三方库引发了强制布局。
- 批量读写DOM(通过 FastDOM or a virtual DOM)。在每帧的开始读取会引发布局属性的值(例如会频繁多次调用的 requestAnimationFrame,scroll 回调函数等之前就读取好),当这些属性的值在最终布局后依然不变。(举个例子:当父元素宽度为100px,for 循环修改所有的子元素宽度为父元素的一半,由于在修改每个子元素的过程中,仍然取父元素的一半 50px,这个时候就应该在 for 之前读取。如果在 for 循环之中读取,由于你修改了子元素的属性,浏览器无法确定对父元素有没有影响,只能强制重排一次来确定(仍然是 50px )。特别是在循环次数比较多的场景,性能会极差。)
跨浏览器 Cross-browser
- 以上数据信息取自 Blink 引擎源代码,所以对大多数浏览器都是真实的。
- 2011 WebKit 情况和以上差不多。Tony Gentilcore's Layout Triggering List
- 现代 WebKit 引擎强制布局行为基本一致。
updateLayoutIgnorePendingStylesheets
- GitHub search - WebKit/WebKit - Gecko 引擎重排操作似乎通过 FrameNeedsReflow。
FrameNeedsReflow
- mozilla-central search - 没有IE具体的数据,现代浏览器行为基本遵行规范,行为大体还是一致的,只是在优化方面稍有不同。
浏览 Chromium 源代码
- 强制重排(样式计算):
UpdateStyleAndLayoutIgnorePendingStylesheets
- Chromium Code Search - 强制样式计算:
UpdateStyleAndLayoutTreeIgnorePendingStylesheets
- Chromium Code Search
CSS Triggers
CSS Triggers 是一个重要的资源网站,描述了哪些样式改变会引发渲染管道哪些周期会被执行。以上讲的 JS 操作导致强制重排都会引发渲染管道中的布局、绘制、合成三者同步执行。
更多有关强制布局的文章 More on forced layout
- Avoiding layout thrashing — Web Fundamentals
- Fixing Layout thrashing in the real world | Matt Andrews
- Timeline demo: Diagnosing forced synchronous layouts - Google Chrome
- Preventing 'layout thrashing' | Wilson Page
- wilsonpage/fastdom
- Rendering: repaint, reflow/relayout, restyle / Stoyan
- We spent a week making Trello boards load extremely fast. Here’s how we did it. - Fog Creek Blog
- Minimizing browser reflow | PageSpeed Insights | Google Developers
- Optimizing Web Content in UIWebViews and Websites on iOS
- Accelerated Rendering in Chrome
- web performance for the curious
- Jank Free
2018.02 修改:代码搜索链接,部分相关元素属性。