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