Files
2026-07-13 21:36:17 +08:00

3.2 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Avoid Layout Thrashing MEDIUM 防止强制同步布局,减少性能瓶颈 javascript, dom, css, performance, reflow, layout-thrashing

避免布局抖动

避免将样式写入与布局读取交错进行。当你在样式更改之间读取布局属性(如 offsetWidthgetBoundingClientRect()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 文件会被浏览器缓存,类能提供更好的关注点分离,且更易于维护。

更多关于强制布局操作的信息,请参见此 GistCSS Triggers