Layout thrashing là việc đọc thuộc tính hình học rồi ghi style ngay, lặp đi lặp lại — mỗi lần đọc buộc browser tính lại layout đồng bộ để trả về số đúng.
// SAI: mỗi vòng lặp là một reflow đồng bộ
for (const el of items) {
el.style.height = el.offsetHeight + 10 + 'px' // đọc → ghi → đọc → ghi ...
}
// ĐÚNG: đọc hết trước, ghi sau
const heights = items.map((el) => el.offsetHeight) // đọc theo lô
items.forEach((el, i) => { el.style.height = heights[i] + 10 + 'px' }) // ghi theo lôBrowser vốn gộp các thay đổi style và chỉ layout một lần trước khi vẽ khung hình. Nhưng khi bạn hỏi một giá trị phụ thuộc layout, nó buộc phải tính ngay để trả lời — cơ chế gộp bị phá.
Nhóm thuộc tính buộc reflow khi đọc: offsetTop/Left/Width/Height, clientTop/Left/Width/Height, scrollTop/Left/Width/Height, getComputedStyle(), getBoundingClientRect(), focus().
Ba cách xử lý:
1. Tách đọc và ghi thành hai pha như ví dụ trên.
2. Dùng requestAnimationFrame để dồn phần ghi vào đúng đầu khung hình.
3. Dùng transform/opacity thay top/left/width — không sinh layout.
Mẹo phát hiện: Performance panel trong DevTools đánh dấu "Forced reflow" kèm stack trace ngay tại dòng code gây ra. Không cần đoán.