Ngày xuất bản: 20/3/2015, Ngày cập nhật gần đây nhất: 7/5/2025
Bố cục là nơi trình duyệt tìm ra thông tin hình học cho các phần tử: kích thước và vị trí của các phần tử đó trong trang. Mỗi phần tử sẽ có thông tin định cỡ rõ ràng hoặc ngầm ẩn dựa trên CSS đã sử dụng, nội dung của phần tử hoặc phần tử mẹ. Quy trình này được gọi là Bố cục trong Chrome (và các trình duyệt phái sinh như Edge) và Safari. Trong Firefox, tính năng này được gọi là Reflow (Lưu lại luồng), nhưng quy trình này về cơ bản vẫn giống nhau.
Tương tự như việc tính toán kiểu, những vấn đề cấp thiết về chi phí bố cục là:
- Số lượng phần tử cần bố cục, đây là sản phẩm phụ của kích thước DOM của trang.
- Mức độ phức tạp của các bố cục đó.
Tóm tắt
- Bố cục có ảnh hưởng trực tiếp đến độ trễ tương tác
- Bố cục thường được áp dụng cho toàn bộ tài liệu.
- Số lượng phần tử DOM sẽ ảnh hưởng đến hiệu suất; bạn nên tránh kích hoạt bố cục bất cứ khi nào có thể.
- Tránh bố cục đồng bộ bắt buộc và bố cục bị lỗi; đọc giá trị kiểu rồi thực hiện thay đổi kiểu.
Ảnh hưởng của bố cục đối với độ trễ tương tác
Khi người dùng tương tác với trang, các lượt tương tác đó phải diễn ra nhanh nhất có thể. Khoảng thời gian cần thiết để hoàn tất một lượt tương tác (kết thúc khi trình duyệt hiển thị khung hình tiếp theo để cho thấy kết quả của lượt tương tác) được gọi là độ trễ tương tác. Đây là một khía cạnh của hiệu suất trang mà chỉ số Lượt tương tác đến nội dung hiển thị tiếp theo đo lường.
Khoảng thời gian trình duyệt hiển thị khung hình tiếp theo để phản hồi hoạt động tương tác của người dùng được gọi là độ trễ hiển thị của hoạt động tương tác. Mục tiêu của một lượt tương tác là cung cấp phản hồi trực quan để báo hiệu cho người dùng rằng đã có điều gì đó xảy ra. Việc cập nhật hình ảnh có thể liên quan đến một số công việc bố cục để đạt được mục tiêu đó.
Để giảm thiểu INP của trang web, bạn cần tránh sử dụng bố cục khi có thể. Nếu không thể tránh hoàn toàn bố cục, điều quan trọng là bạn phải giới hạn công việc bố cục đó để trình duyệt có thể hiển thị khung hình tiếp theo một cách nhanh chóng.
Tránh bố cục bất cứ khi nào có thể
Khi bạn thay đổi kiểu, trình duyệt sẽ kiểm tra xem có thay đổi nào yêu cầu tính toán bố cục và cập nhật cây kết xuất đó hay không. Mọi thay đổi đối với "thuộc tính hình học", chẳng hạn như width
, height
, left
hoặc top
đều yêu cầu bố cục.
.box {
width: 20px;
height: 20px;
}
/**
* Changing width and height
* triggers layout.
*/
.box--expanded {
width: 200px;
height: 350px;
}
Bố cục hầu như luôn nằm trong phạm vi toàn bộ tài liệu. Nếu bạn có nhiều phần tử, bạn sẽ mất nhiều thời gian để tìm ra vị trí và kích thước của tất cả các phần tử đó.
Nếu không thể tránh được bố cục, thì điều quan trọng là bạn phải sử dụng lại Công cụ của Chrome cho nhà phát triển để xem thời gian cần thiết và xác định xem bố cục có phải là nguyên nhân gây ra nút thắt cổ chai hay không. Trước tiên, hãy mở DevTools, chuyển đến thẻ Dòng thời gian, nhấn vào nút ghi và tương tác với trang web của bạn. Khi dừng ghi, bạn sẽ thấy thông tin chi tiết về hiệu suất của trang web:

Khi tìm hiểu dấu vết trong ví dụ trước, chúng ta thấy rằng hơn 28 mili giây được dùng bên trong bố cục cho mỗi khung hình, khi chúng ta có 16 mili giây để hiển thị một khung hình trên màn hình trong ảnh động, thì thời gian này là quá cao. Bạn cũng có thể thấy rằng DevTools sẽ cho bạn biết kích thước cây (1.618 phần tử trong trường hợp này) và số lượng nút cần bố cục (5 trong trường hợp này).
Xin lưu ý rằng lời khuyên chung ở đây là tránh bố cục bất cứ khi nào có thể — nhưng không phải lúc nào bạn cũng có thể tránh bố cục. Trong trường hợp bạn không thể tránh bố cục, hãy lưu ý rằng chi phí bố cục có mối quan hệ với kích thước của DOM. Mặc dù mối quan hệ giữa hai thành phần này không được ghép nối chặt chẽ, nhưng DOM lớn hơn thường sẽ gây ra chi phí bố cục cao hơn.
Tránh bố cục đồng bộ bắt buộc
Việc vận chuyển khung đến màn hình có thứ tự như sau:

Trước tiên, JavaScript chạy, sau đó tính toán kiểu, sau đó bố cục. Tuy nhiên, bạn có thể buộc trình duyệt thực hiện bố cục sớm hơn bằng JavaScript. Đây được gọi là bố cục đồng bộ bắt buộc (hoặc đôi khi là lưu lại bắt buộc).
Điều đầu tiên cần lưu ý là khi JavaScript chạy, tất cả các giá trị bố cục cũ từ khung trước đó đều đã được biết và có sẵn để bạn truy vấn. Ví dụ: nếu muốn viết chiều cao của một phần tử (gọi là "hộp") ở đầu khung, bạn có thể viết một số mã như sau:
// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);
function logBoxHeight () {
// Gets the height of the box in pixels and logs it out:
console.log(box.offsetHeight);
}
Sẽ có vấn đề nếu bạn đã thay đổi kiểu của hộp trước khi yêu cầu chiều cao của hộp:
function logBoxHeight () {
box.classList.add('super-big');
// Gets the height of the box in pixels and logs it out:
console.log(box.offsetHeight);
}
Bây giờ, để trả lời câu hỏi về chiều cao, trước tiên, trình duyệt phải áp dụng thay đổi về kiểu (do thêm lớp super-big
) và sau đó chạy bố cục. Chỉ khi đó, hàm này mới có thể trả về chiều cao chính xác. Đây là công việc không cần thiết và có thể tốn kém.
Do đó, bạn phải luôn thực hiện các lượt đọc kiểu theo lô và thực hiện các lượt đọc đó trước tiên (trong đó trình duyệt có thể sử dụng các giá trị bố cục của khung trước đó), sau đó thực hiện mọi lượt ghi:
Phiên bản hiệu quả hơn của hàm trước đó sẽ là:
function logBoxHeight () {
// Gets the height of the box in pixels and logs it out:
console.log(box.offsetHeight);
box.classList.add('super-big');
}
Trong hầu hết trường hợp, bạn không cần áp dụng kiểu rồi truy vấn giá trị; việc sử dụng giá trị của khung cuối cùng là đủ. Việc chạy các phép tính kiểu và bố cục đồng bộ và sớm hơn so với mong muốn của trình duyệt có thể là nút thắt cổ chai tiềm ẩn và không phải là điều bạn thường muốn làm.
Tránh tình trạng bố cục bị đơ
Có một cách để làm cho bố cục đồng bộ bắt buộc trở nên tệ hơn nữa: thực hiện nhiều bố cục như vậy liên tiếp. Hãy xem mã sau:
function resizeAllParagraphsToMatchBlockWidth () {
// Puts the browser into a read-write-read-write cycle.
for (let i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = `${box.offsetWidth}px`;
}
}
Mã này lặp lại trên một nhóm các đoạn văn và đặt chiều rộng của mỗi đoạn văn sao cho khớp với chiều rộng của một phần tử có tên là "hộp". Mã này có vẻ không gây hại, nhưng vấn đề là mỗi lần lặp lại của vòng lặp sẽ đọc một giá trị kiểu (box.offsetWidth
) rồi ngay lập tức sử dụng giá trị đó để cập nhật chiều rộng của một đoạn văn bản (paragraphs[i].style.width
). Ở lần lặp lại tiếp theo của vòng lặp, trình duyệt phải tính đến việc các kiểu đã thay đổi kể từ lần yêu cầu offsetWidth
gần đây nhất (trong lần lặp lại trước đó), do đó, trình duyệt phải áp dụng các thay đổi về kiểu và chạy bố cục. Điều này sẽ xảy ra trong mỗi lần lặp lại!
Cách khắc phục cho mẫu này là một lần nữa đọc rồi ghi các giá trị:
// Read.
const width = box.offsetWidth;
function resizeAllParagraphsToMatchBlockWidth () {
for (let i = 0; i < paragraphs.length; i++) {
// Now write.
paragraphs[i].style.width = `${width}px`;
}
}
Xác định bố cục đồng bộ bắt buộc và tình trạng đơ
Công cụ phát triển có thông tin chi tiết về Buộc luồng lại để giúp bạn nhanh chóng xác định các trường hợp bố cục đồng bộ bị buộc (còn gọi là "buộc luồng lại"):

Bạn cũng có thể xác định bố cục đồng bộ bắt buộc trong trường bằng cách sử dụng thuộc tính tập lệnh API Khung ảnh động dài bằng thuộc tính forcedStyleAndLayoutDuration
.