Gỡ lỗi thay đổi bố cục

Tìm hiểu cách xác định và khắc phục vấn đề thay đổi bố cục.

Katie Hempenius
Katie Hempenius

Phần đầu tiên của bài viết này thảo luận về công cụ để gỡ lỗi thay đổi bố cục, còn phần thứ hai thảo luận về quy trình tư duy cần sử dụng khi xác định nguyên nhân dẫn đến việc thay đổi bố cục.

Công cụ

Layout Instability API (API Không ổn định của bố cục)

Layout Instability API là cơ chế trình duyệt để đo lường và báo cáo sự thay đổi bố cục. Tất cả các công cụ để gỡ lỗi về việc thay đổi bố cục, bao gồm cả Công cụ cho nhà phát triển, cuối cùng được xây dựng dựa trên Layout Không ổn định của bố cục. Tuy nhiên, việc sử dụng trực tiếp Layout Instability API là một công cụ gỡ lỗi hữu ích nhờ tính linh hoạt của API này.

Cách sử dụng

Cùng một đoạn mã đo lường Điểm số tổng hợp về mức thay đổi bố cục (CLS) cũng có thể phân phát để gỡ lỗi về mức thay đổi bố cục. Đoạn mã dưới đây sẽ ghi lại thông tin về việc thay đổi bố cục sang bảng điều khiển. Khi kiểm tra nhật ký này, bạn sẽ biết được thông tin về thời điểm, vị trí và cách thức diễn ra sự thay đổi bố cục.

let cls = 0;
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      cls += entry.value;
      console.log('Current CLS value:', cls, entry);
    }
  }
}).observe({type: 'layout-shift', buffered: true});

Khi chạy tập lệnh này, hãy lưu ý rằng:

  • Tuỳ chọn buffered: true cho biết rằng PerformanceObserver sẽ kiểm tra vùng đệm mục hiệu suất của trình duyệt để biết các mục hiệu suất đã được tạo trước khi khởi chạy của trình quan sát. Do đó, PerformanceObserver sẽ báo cáo các thay đổi về bố cục xảy ra cả trước và sau khi khởi chạy. Hãy lưu ý điều này khi kiểm tra nhật ký bảng điều khiển. Tình trạng thay đổi bố cục ban đầu có thể phản ánh tình trạng tồn đọng báo cáo, thay vì sự xuất hiện đột ngột của nhiều lần thay đổi bố cục.
  • Để tránh ảnh hưởng đến hiệu suất, PerformanceObserver sẽ chờ cho đến khi luồng chính ở trạng thái rảnh để báo cáo về sự thay đổi bố cục. Do đó, tuỳ thuộc vào mức độ bận rộn của luồng chính, có thể có một chút chậm trễ giữa thời điểm thay đổi bố cục và thời điểm ghi lại bố cục vào bảng điều khiển.
  • Tập lệnh này bỏ qua việc thay đổi bố cục xảy ra trong vòng 500 mili giây từ hoạt động đầu vào của người dùng và do đó, không được tính vào CLS.

Thông tin về việc thay đổi bố cục được báo cáo bằng cách sử dụng kết hợp 2 API: giao diện LayoutShiftLayoutShiftAttribution. Từng giao diện này sẽ được giải thích chi tiết hơn trong các phần sau.

LayoutShift

Mỗi lần thay đổi bố cục được báo cáo bằng giao diện LayoutShift. Nội dung của mục nhập sẽ có dạng như sau:

duration: 0
entryType: "layout-shift"
hadRecentInput: false
lastInputTime: 0
name: ""
sources: (3) [LayoutShiftAttribution, LayoutShiftAttribution, LayoutShiftAttribution]
startTime: 11317.934999999125
value: 0.17508567530168798

Mục nhập ở trên cho thấy một sự thay đổi bố cục trong đó 3 phần tử DOM đã thay đổi vị trí. Điểm số về mức thay đổi bố cục của lần thay đổi bố cục cụ thể này là 0.175.

Dưới đây là các thuộc tính của thực thể LayoutShift liên quan nhất đến việc gỡ lỗi về việc thay đổi bố cục:

Tài sản Nội dung mô tả
sources Thuộc tính sources liệt kê các phần tử DOM đã di chuyển trong quá trình thay đổi bố cục. Mảng này có thể chứa tối đa 5 nguồn. Trong trường hợp có nhiều hơn 5 yếu tố bị ảnh hưởng bởi việc thay đổi bố cục, 5 nguồn lớn nhất (theo mức độ tác động đến độ ổn định của bố cục) sẽ làm thay đổi bố cục. Thông tin này được báo cáo bằng giao diện LayoutShiftAttribution (được giải thích chi tiết hơn bên dưới).
value Thuộc tính value báo cáo điểm thay đổi bố cục cho một lần thay đổi bố cục cụ thể.
hadRecentInput Thuộc tính hadRecentInput cho biết liệu thay đổi bố cục có diễn ra trong vòng 500 mili giây kể từ khi người dùng nhập hay không.
startTime Thuộc tính startTime cho biết thời điểm diễn ra sự thay đổi bố cục. startTime được biểu thị bằng mili giây và được đo lường theo thời gian bắt đầu tải trang.
duration Thuộc tính duration sẽ luôn được thiết lập thành 0. Thuộc tính này được kế thừa từ giao diện PerformanceEntry (giao diện LayoutShift mở rộng giao diện PerformanceEntry). Tuy nhiên, khái niệm thời lượng không áp dụng cho các sự kiện thay đổi bố cục, vì vậy, khái niệm thời lượng được thiết lập thành 0. Để biết thông tin về giao diện PerformanceEntry, hãy tham khảo spec.

LayoutShiftAttribution

Giao diện LayoutShiftAttribution mô tả một ca thay đổi duy nhất của một phần tử DOM. Nếu nhiều phần tử thay đổi trong quá trình thay đổi bố cục, thì thuộc tính sources sẽ chứa nhiều mục nhập.

Ví dụ: JSON dưới đây tương ứng với sự thay đổi bố cục với một nguồn: sự dịch chuyển xuống dưới của phần tử DOM <div id='banner'> từ y: 76 sang y:246.

// ...
  "sources": [
    {
      "node": "div#banner",
      "previousRect": {
        "x": 311,
        "y": 76,
        "width": 4,
        "height": 18,
        "top": 76,
        "right": 315,
        "bottom": 94,
        "left": 311
      },
      "currentRect": {
        "x": 311,
        "y": 246,
        "width": 4,
        "height": 18,
        "top": 246,
        "right": 315,
        "bottom": 264,
        "left": 311
      }
    }
  ]

Thuộc tính node xác định phần tử HTML đã dịch chuyển. Khi di chuột vào tài sản này trong Công cụ cho nhà phát triển, phần tử trang tương ứng sẽ được làm nổi bật.

Các thuộc tính previousRectcurrentRect báo cáo kích thước và vị trí của nút.

  • Các toạ độ xy báo cáo toạ độ x và toạ độ y tương ứng ở góc trên cùng bên trái của phần tử
  • Các thuộc tính widthheight báo cáo chiều rộng và chiều cao tương ứng của phần tử.
  • Các thuộc tính top, right, bottomleft báo cáo giá trị toạ độ x hoặc y tương ứng với cạnh đã cho của phần tử. Nói cách khác, giá trị của top bằng y; giá trị của bottom bằng y+height.

Nếu tất cả thuộc tính của previousRect được đặt thành 0, thì điều này có nghĩa là phần tử đó đã chuyển sang khung hiển thị. Nếu tất cả thuộc tính của currentRect được đặt thành 0, điều này có nghĩa là phần tử đã dịch chuyển ra khỏi khung hiển thị.

Một trong những điều quan trọng nhất cần hiểu rõ khi diễn giải các kết quả này là các phần tử được liệt kê dưới dạng nguồn là các phần tử thay đổi trong quá trình thay đổi bố cục. Tuy nhiên, có thể các phần tử này chỉ liên quan gián tiếp vào "nguyên nhân gốc" khiến bố cục mất ổn định. Sau đây là một vài ví dụ.

Ví dụ 1

Sự thay đổi bố cục này sẽ được báo cáo bằng một nguồn: phần tử B. Tuy nhiên, nguyên nhân gốc rễ của sự thay đổi bố cục này là sự thay đổi về kích thước của phần tử A.

Ví dụ minh hoạ sự thay đổi bố cục do thay đổi kích thước của phần tử

Ví dụ 2

Sự thay đổi bố cục trong ví dụ này sẽ được báo cáo bằng 2 nguồn: phần tử A và phần tử B. Nguyên nhân gốc rễ của việc thay đổi bố cục này là sự thay đổi về vị trí của phần tử A.

Ví dụ minh hoạ sự thay đổi bố cục do sự thay đổi vị trí của phần tử

Ví dụ 3

Sự thay đổi bố cục trong ví dụ này sẽ được báo cáo bằng một nguồn: phần tử B. Việc thay đổi vị trí của phần tử B đã dẫn đến sự thay đổi bố cục này.

Ví dụ minh hoạ sự thay đổi bố cục do sự thay đổi vị trí của phần tử

Ví dụ 4

Mặc dù phần tử B thay đổi kích thước, nhưng không có sự thay đổi về bố cục trong ví dụ này.

Ví dụ cho thấy một phần tử thay đổi kích thước nhưng không làm thay đổi bố cục

Hãy xem bản minh hoạ cách Layout Instability API báo cáo các thay đổi của DOM.

DevTools

Bảng điều khiển hiệu suất

Ngăn Trải nghiệm của bảng Hiệu suất trong công cụ cho nhà phát triển hiển thị tất cả các lần thay đổi bố cục xảy ra trong một dấu vết hiệu suất nhất định, ngay cả khi chúng xảy ra trong vòng 500 mili giây kể từ khi người dùng tương tác và do đó không được tính vào CLS. Khi di chuột qua một lần thay đổi bố cục cụ thể trong bảng Trải nghiệm, phần tử DOM bị ảnh hưởng sẽ được làm nổi bật.

Ảnh chụp màn hình về việc thay đổi bố cục hiển thị trong bảng điều khiển Mạng cho nhà phát triển

Để xem thêm thông tin về thay đổi bố cục, hãy nhấp vào thay đổi bố cục đó, sau đó mở ngăn Tóm tắt. Các thay đổi đối với kích thước của phần tử được liệt kê bằng định dạng [width, height]; các thay đổi đối với vị trí của phần tử được liệt kê bằng định dạng [x,y]. Thuộc tính Có đầu vào gần đây cho biết liệu sự thay đổi bố cục có xảy ra trong vòng 500 mili giây kể từ khi người dùng tương tác hay không.

Ảnh chụp màn hình thẻ &quot;Tóm tắt&quot; của Công cụ cho nhà phát triển cho quá trình thay đổi bố cục

Để biết thông tin về khoảng thời gian thay đổi bố cục, hãy mở thẻ Nhật ký sự kiện. Bạn cũng có thể ước chừng thời gian của một lần thay đổi bố cục bằng cách xem trong ngăn Trải nghiệm để biết chiều dài của hình chữ nhật thay đổi bố cục màu đỏ.

Ảnh chụp màn hình thẻ &quot;Nhật ký sự kiện&quot; của Công cụ cho nhà phát triển cho quá trình thay đổi bố cục

Để biết thêm thông tin về cách sử dụng bảng điều khiển Hiệu suất, hãy tham khảo Tài liệu tham khảo về Phân tích hiệu suất.

Làm nổi bật các khu vực thay đổi bố cục

Làm nổi bật các khu vực thay đổi bố cục có thể là một kỹ thuật hữu ích để nhanh chóng nhận biết vị trí và thời gian thay đổi bố cục đang xảy ra trên trang.

Để bật tính năng Khu vực thay đổi bố cục trong Công cụ cho nhà phát triển, hãy chuyển đến phần Cài đặt > Công cụ khác > Hiển thị > Khu vực dịch chuyển bố cục sau đó làm mới trang mà bạn muốn gỡ lỗi. Các vùng của thay đổi bố cục sẽ được làm nổi bật nhanh bằng màu tím.

Quy trình suy nghĩ để xác định nguyên nhân của việc thay đổi bố cục

Bạn có thể làm theo các bước bên dưới để xác định nguyên nhân gây ra việc thay đổi bố cục bất kể thời điểm hoặc cách thức thay đổi bố cục. Bạn có thể bổ sung các bước này bằng việc chạy Lighthouse. Tuy nhiên, hãy lưu ý rằng Lighthouse chỉ có thể xác định thay đổi bố cục xảy ra trong lần tải trang ban đầu. Ngoài ra, Lighthouse cũng chỉ có thể cung cấp đề xuất cho một số nguyên nhân gây ra sự thay đổi bố cục – ví dụ: các phần tử hình ảnh không có chiều rộng và chiều cao rõ ràng.

Xác định nguyên nhân dẫn đến việc thay đổi bố cục

Việc thay đổi bố cục có thể do các sự kiện sau gây ra:

  • Thay đổi đối với vị trí của phần tử Mô hình đối tượng tài liệu (DOM)
  • Các thay đổi về phương diện của phần tử Mô hình đối tượng tài liệu (DOM)
  • Chèn hoặc xoá phần tử DOM
  • Ảnh động kích hoạt bố cục

Cụ thể, phần tử DOM ngay trước phần tử được dịch chuyển là phần tử có nhiều khả năng tham gia vào việc "gây ra" việc thay đổi bố cục nhất. Do đó, khi điều tra lý do xảy ra sự thay đổi bố cục, hãy xem xét:

  • Vị trí hoặc phương diện của phần tử trước đó có thay đổi không?
  • Phần tử DOM có được chèn hoặc bị xoá trước phần tử được dịch chuyển không?
  • Vị trí của phần tử được dịch chuyển có bị thay đổi rõ ràng không?

Nếu phần tử trước đó không gây ra sự thay đổi về bố cục, hãy tiếp tục tìm kiếm bằng cách xem xét các phần tử ở trước và lân cận.

Ngoài ra, hướng và khoảng cách của thay đổi bố cục có thể cung cấp gợi ý về nguyên nhân gốc. Ví dụ: sự thay đổi lớn xuống dưới thường biểu thị phần chèn phần tử DOM, trong khi sự thay đổi bố cục 1 px hoặc 2 px thường biểu thị việc áp dụng các kiểu CSS xung đột hoặc việc tải và áp dụng phông chữ web.

Sơ đồ cho thấy sự thay đổi bố cục do việc hoán đổi phông chữ gây ra
Trong ví dụ này, việc hoán đổi phông chữ làm cho các phần tử trang dịch chuyển lên trên 5 pixel.

Dưới đây là một số hành vi cụ thể thường xuyên gây ra các sự kiện thay đổi bố cục:

Thay đổi đối với vị trí của một phần tử (không phải do sự di chuyển của một phần tử khác)

Loại thay đổi này thường là kết quả của:

  • Các biểu định kiểu được tải muộn hoặc ghi đè các kiểu đã khai báo trước đó.
  • Hiệu ứng chuyển tiếp và ảnh động.

Các thay đổi về kích thước của một phần tử

Loại thay đổi này thường là kết quả của:

  • Các biểu định kiểu được tải muộn hoặc ghi đè các kiểu đã khai báo trước đó.
  • Hình ảnh và iframe không có các thuộc tính widthheight sẽ tải sau khi kết xuất "ô".
  • Các khối văn bản không có thuộc tính width hoặc height có thể hoán đổi phông chữ sau khi hiển thị văn bản.

Chèn hoặc xoá các phần tử Mô hình đối tượng tài liệu (DOM)

Điều này thường là kết quả của:

  • Chèn quảng cáo và nội dung nhúng khác của bên thứ ba.
  • Chèn biểu ngữ, cảnh báo và phương thức.
  • Cuộn vô hạn và các mẫu trải nghiệm người dùng khác tải thêm nội dung phía trên nội dung hiện có.

Ảnh động kích hoạt bố cục

Một số hiệu ứng ảnh động có thể kích hoạt bố cục. Một ví dụ phổ biến về vấn đề này là khi các phần tử DOM được "ảnh động" bằng cách tăng các thuộc tính như top hoặc left thay vì sử dụng thuộc tính transform của CSS. Đọc bài viết Cách tạo ảnh động CSS hiệu suất cao để biết thêm thông tin.

Tái tạo các thay đổi về bố cục

Bạn không thể sửa lỗi thay đổi bố cục mà bạn không mô phỏng được. Một trong những cách đơn giản nhất nhưng hiệu quả nhất mà bạn có thể làm để hiểu rõ hơn về độ ổn định của bố cục trang web là mất 5-10 phút để tương tác với trang web với mục tiêu kích hoạt việc thay đổi bố cục. Luôn mở bảng điều khiển trong khi thực hiện việc này và sử dụng Layout Instability API để báo cáo về thay đổi bố cục.

Để khó xác định vị trí thay đổi bố cục, hãy cân nhắc lặp lại bài tập thể dục này với các thiết bị và tốc độ kết nối khác nhau. Cụ thể, việc sử dụng tốc độ kết nối chậm hơn có thể giúp bạn dễ dàng xác định việc thay đổi bố cục. Ngoài ra, bạn có thể sử dụng câu lệnh debugger để dễ dàng thực hiện các bước thay đổi bố cục.

new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      cls += entry.value;
      debugger;
      console.log('Current CLS value:', cls, entry);
    }
  }
}).observe({type: 'layout-shift', buffered: true});

Cuối cùng, đối với các vấn đề về bố cục không thể tái tạo trong quá trình phát triển, hãy cân nhắc sử dụng Layout Instability API cùng với công cụ ghi nhật ký giao diện người dùng mà bạn chọn để thu thập thêm thông tin về các vấn đề này. Xem mã ví dụ để biết cách theo dõi phần tử có sự dịch chuyển lớn nhất trên một trang.