3.2 KiB
3.2 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Avoid Layout Thrashing | MEDIUM | 防止强制同步布局,减少性能瓶颈 | javascript, dom, css, performance, reflow, layout-thrashing |
避免布局抖动
避免将样式写入与布局读取交错进行。当你在样式更改之间读取布局属性(如 offsetWidth、getBoundingClientRect() 或 getComputedStyle())时,浏览器会被迫触发同步重排。
这样是可以的(浏览器会批量处理样式更改):
function updateElementStyles(element: HTMLElement) {
// 每一行都会使样式失效,但浏览器会批量重新计算
element.style.width = '100px'
element.style.height = '200px'
element.style.backgroundColor = 'blue'
element.style.border = '1px solid black'
}
错误做法(读写交错会强制重排):
function layoutThrashing(element: HTMLElement) {
element.style.width = '100px'
const width = element.offsetWidth // 强制重排
element.style.height = '200px'
const height = element.offsetHeight // 再次强制重排
}
正确做法(批量写入,然后一次性读取):
function updateElementStyles(element: HTMLElement) {
// 将所有写入操作批量处理
element.style.width = '100px'
element.style.height = '200px'
element.style.backgroundColor = 'blue'
element.style.border = '1px solid black'
// 所有写入完成后读取(仅一次重排)
const { width, height } = element.getBoundingClientRect()
}
正确做法(批量读取,然后再写入):
function avoidThrashing(element: HTMLElement) {
// 读取阶段——先执行所有布局查询
const rect1 = element.getBoundingClientRect()
const offsetWidth = element.offsetWidth
const offsetHeight = element.offsetHeight
// 写入阶段——之后再执行所有样式更改
element.style.width = '100px'
element.style.height = '200px'
}
更好的做法:使用 CSS 类
.highlighted-box {
width: 100px;
height: 200px;
background-color: blue;
border: 1px solid black;
}
function updateElementStyles(element: HTMLElement) {
element.classList.add('highlighted-box')
const { width, height } = element.getBoundingClientRect()
}
React 示例:
// 错误做法:将样式更改与布局查询交错进行
function Box({ isHighlighted }: { isHighlighted: boolean }) {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if (ref.current && isHighlighted) {
ref.current.style.width = '100px'
const width = ref.current.offsetWidth // 强制布局
ref.current.style.height = '200px'
}
}, [isHighlighted])
return <div ref={ref}>Content</div>
}
// 正确做法:切换类名
function Box({ isHighlighted }: { isHighlighted: boolean }) {
return (
<div className={isHighlighted ? 'highlighted-box' : ''}>
Content
</div>
)
}
尽可能优先使用 CSS 类而非内联样式。CSS 文件会被浏览器缓存,类能提供更好的关注点分离,且更易于维护。
更多关于强制布局操作的信息,请参见此 Gist 和 CSS Triggers。