fb79a15508
- 添加 sections 定义文件,包含性能优化各领域分类 - 添加规则模板文件,规范文档结构和标签定义 - 添加异步操作优化规则,包括防止瀑布流、并行化、延迟等待等 - 添加包大小优化规则,包括避免桶式导入、动态导入、预加载等 - 添加服务端性能优化规则,包括 API 路
3.2 KiB
3.2 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Avoid Layout Thrashing | MEDIUM | prevents forced synchronous layouts and reduces performance bottlenecks | javascript, dom, css, performance, reflow, layout-thrashing |
Avoid Layout Thrashing
Avoid interleaving style writes with layout reads. When you read a layout property (like offsetWidth, getBoundingClientRect(), or getComputedStyle()) between style changes, the browser is forced to trigger a synchronous reflow.
This is OK (browser batches style changes):
function updateElementStyles(element: HTMLElement) {
// Each line invalidates style, but browser batches the recalculation
element.style.width = '100px'
element.style.height = '200px'
element.style.backgroundColor = 'blue'
element.style.border = '1px solid black'
}
Incorrect (interleaved reads and writes force reflows):
function layoutThrashing(element: HTMLElement) {
element.style.width = '100px'
const width = element.offsetWidth // Forces reflow
element.style.height = '200px'
const height = element.offsetHeight // Forces another reflow
}
Correct (batch writes, then read once):
function updateElementStyles(element: HTMLElement) {
// Batch all writes together
element.style.width = '100px'
element.style.height = '200px'
element.style.backgroundColor = 'blue'
element.style.border = '1px solid black'
// Read after all writes are done (single reflow)
const { width, height } = element.getBoundingClientRect()
}
Correct (batch reads, then writes):
function avoidThrashing(element: HTMLElement) {
// Read phase - all layout queries first
const rect1 = element.getBoundingClientRect()
const offsetWidth = element.offsetWidth
const offsetHeight = element.offsetHeight
// Write phase - all style changes after
element.style.width = '100px'
element.style.height = '200px'
}
Better: use CSS classes
.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 example:
// Incorrect: interleaving style changes with layout queries
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 // Forces layout
ref.current.style.height = '200px'
}
}, [isHighlighted])
return <div ref={ref}>Content</div>
}
// Correct: toggle class
function Box({ isHighlighted }: { isHighlighted: boolean }) {
return (
<div className={isHighlighted ? 'highlighted-box' : ''}>
Content
</div>
)
}
Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.
See this gist and CSS Triggers for more information on layout-forcing operations.