Tránh bố cục lớn, phức tạp và tình trạng bập bênh bố cục

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ử đó trên trang. Mỗi phần tử sẽ có thông tin kích thước rõ ràng hoặc ngầm ẩn dựa trên CSS đã được sử dụng, nội dung của phần tử hoặc phần tử mẹ. Quá trình này được gọi là Bố cục trong Chrome.

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ử đó trên trang. Mỗi phần tử sẽ có thông tin kích thước rõ ràng hoặc ngầm ẩn dựa trên CSS đã được sử dụng, nội dung của phần tử hoặc phần tử mẹ. Quá trình này được gọi là Bố cục trong Chrome (và các trình duyệt phát sinh như Edge) và Safari. Trong Firefox, trình duyệt này được gọi là Reflow, nhưng quá trình này cũng tương tự như vậy.

Tương tự như các phép tính kiểu, những mối lo ngại tức thì về chi phí bố cục là:

  1. Số lượng phần tử cần có bố cục, là sản phẩm phụ của kích thước DOM của trang.
  2. Độ phức tạp của những 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 nằm trong phạm vi 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à tình trạng đơ bố cục; hãy đọc các giá trị kiểu rồi 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 để một tương tác hoàn tất—kết thúc khi trình duyệt hiển thị khung tiếp theo để hiển thị kết quả của tương tác—được gọi là độ trễ tương tác. Đây là một khía cạnh về hiệu suất trang mà chỉ số Lượt tương tác với nội dung hiển thị tiếp theo đo lường được.

Khoảng thời gian mà trình duyệt cần để hiển thị khung hình tiếp theo để phản hồi tương tác của người dùng được gọi là độ trễ hiển thị của 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 bằng hình ảnh để báo hiệu cho người dùng rằng điều gì đó đã xảy ra, đồng thời việc cập nhật bằng hình ảnh có thể cần đến một số công việc về bố cục để đạt được mục tiêu đó.

Để giữ INP của trang web thấp nhất có thể, 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, bạn cần phải hạn chế 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. Các thay đổi đối với "thuộc tính hình học", chẳng hạn như chiều rộng, chiều cao, bên trái hoặc trên cùng đều cần có 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ử, sẽ mất nhiều thời gian để xác định vị trí và kích thước của tất cả các phần tử đó.

Nếu không thể tránh bố cục thì điều quan trọng là một lần nữa sử dụng 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ở Công cụ cho nhà phát triển, chuyển đến thẻ Dòng thời gian, nhấn vào ghi lại và tương tác với trang web của bạn. Khi dừng ghi, bạn sẽ thấy bảng chi tiết về hiệu suất của trang web:

Công cụ cho nhà phát triển hiển thị thời gian dài trong Bố cục.

Khi đào sâu vào dấu vết trong ví dụ trên, chúng ta thấy rằng hơn 28 mili giây được dành cho bố cục cho mỗi khung hình, tức là khi chúng ta có 16 mili giây để có một khung hình trên màn hình trong ảnh động, là quá cao. Bạn cũng có thể thấy Công cụ cho nhà phát triển 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 sử dụng 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 lớp này không được kết hợp chặt chẽ, nhưng các DOM lớn hơn thường sẽ phải chịu 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 một khung hình tới màn hình có thứ tự sau:

Sử dụng flexbox làm bố cục.

Trước tiên, JavaScript chạy, sau đó tính toán kiểu, sau đó là bố cục. Tuy nhiê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.

Điều đầu tiên cần lưu ý là khi JavaScript chạy, tất cả giá trị bố cục cũ từ khung trước được xác định và có sẵn để bạn truy vấn. Ví dụ: nếu bạn muốn viết ra chiều cao của một phần tử (hãy 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);
}

Mọi thứ sẽ có vấn đề nếu bạn đã thay đổi kiểu của hộp trước khi bạn yêu cầu về 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ình duyệt trước tiên phải áp dụng thay đổi về kiểu (do thêm lớp super-big), rồi sau đó chạy bố cục. Chỉ khi đó, tính năng 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 đọc hàng loạt kiểu của mình và thực hiện trước tiên (trong đó trình duyệt có thể sử dụng giá trị bố cục của khung trước), sau đó thực hiện bất kỳ thao tác ghi nào:

Thực hiện đúng cách, hàm trên 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 sau đó truy vấn các giá trị; chỉ cần sử dụng giá trị của khung cuối cùng là đủ. Chạy các phép tính kiểu và bố cục một cách đồng bộ và sớm hơn trình duyệt mong muốn có thể gây tắc nghẽn và đây không phải là điều mà bạn thường muốn làm.

Tránh tình trạng đơ bố cục

Có một cách để làm cho bố cục đồng bộ bắt buộc trở nên tồi tệ hơn: thực hiện nhanh nhiều bố cục liên tiếp. Hãy xem mã này:

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 qua một nhóm các đoạn và đặt chiều rộng của mỗi đoạn sao cho phù hợp với chiều rộng của phần tử được gọi là "box". Điều này có vẻ vô 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 sử dụng ngay để cập nhật chiều rộng của đoạn (paragraphs[i].style.width). Trong lần lặp tiếp theo của vòng lặp, trình duyệt phải tính đến thực tế là kiểu đã thay đổi kể từ lần cuối offsetWidth được yêu cầu (trong lần lặp trước) và do đó 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 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`;
  }
}

Nếu bạn muốn đảm bảo an toàn, hãy cân nhắc sử dụng FastDOM. Tính năng này tự động phân lô các lượt đọc và ghi cho bạn, đồng thời ngăn bạn vô tình kích hoạt bố cục đồng bộ bắt buộc hoặc tình trạng đơ bố cục.

Hình ảnh chính trong bộ sưu tập Unsplash của Hal Gatewood.