Thử nghiệm A/B phía máy khách không nhấp nháy

Tính năng web: blocking="render"

Vấn đề

Các công cụ thử nghiệm A/B phía máy khách hoạt động bằng cách tải một tập lệnh sửa đổi DOM sau khi trình duyệt đã bắt đầu tạo trang. Nếu không có sự can thiệp, người dùng sẽ thấy nhanh nội dung nguyên gốc trước khi nội dung đó nhấp nháy hoặc chuyển sang biến thể thử nghiệm. Trước đây, các nền tảng thử nghiệm đã giải quyết vấn đề này bằng "đoạn mã chống nhấp nháy" để ẩn toàn bộ trang bằng opacity: 0 cho đến khi tập lệnh thử nghiệm hoàn tất hoặc hết thời gian chờ tuỳ ý (thường là 4 giây). Phương pháp này làm mất khả năng hiển thị tăng dần, cho phép người dùng vô tình tạo lượt nhấp không chủ ý vào nội dung không hiển thị và tạo ra các chu kỳ hiển thị không cần thiết.

Giải pháp

Thuộc tính blocking=render cho phép một phần tử <script> hoặc <link> được đặt trong <head> chặn việc hiển thị (nhưng không chặn việc phân tích cú pháp) cho đến khi tài nguyên được tìm nạp và thực thi. Điều này mang lại cho tập lệnh thử nghiệm hành vi chặn hiển thị giống như biểu định kiểu theo mặc định, đảm bảo trình duyệt không bao giờ hiển thị trang cho đến khi biến thể thử nghiệm được áp dụng. Không có thủ thuật về độ mờ, không có thời gian chờ tuỳ ý và không có hiện tượng nhấp nháy.

Chiến lược triển khai

  1. BẮT BUỘC: Đặt tập lệnh thử nghiệm vào phần tử <head> của tài liệu và thêm blocking="render".
  2. BẮT BUỘC: Đảm bảo tập lệnh có type="module" (ưu tiên cho tập lệnh cùng dòng) hoặc async (ưu tiên cho tập lệnh bên ngoài có giá trị src).
  3. NÊN giữ cho tập lệnh thử nghiệm nhỏ và nhanh. Vì quá trình kết xuất bị chặn cho đến khi tập lệnh thực thi, nên tập lệnh lớn hoặc chậm sẽ trực tiếp làm chậm lần hiển thị đầu tiên.
  4. KHÔNG sử dụng blocking="render" trên các tập lệnh không cần chạy trước lần hiển thị đầu tiên. Thuộc tính này chỉ dành cho những tập lệnh có kết quả đầu ra phải hiển thị trong lần kết xuất ban đầu.
  5. KHÔNG áp dụng blocking="render" cho các tập lệnh bên ngoài <head>. Chỉ các tập lệnh trong <head> mới có thể chặn quá trình kết xuất.

Hướng dẫn triển khai

Thiết lập cơ bản

BẮT BUỘC: Tải tập lệnh thử nghiệm bằng cả async và blocking="render". Thuộc tính async đảm bảo tập lệnh không chặn quá trình phân tích cú pháp HTML (trình duyệt tiếp tục tạo DOM trong khi tìm nạp tập lệnh). Thuộc tính blocking="render" đảm bảo trình duyệt không hiển thị bất kỳ nội dung nào cho đến khi tập lệnh được thực thi.

<head>
  <!--
    MANDATORY: Both `async` and `blocking="render"` are required.
    - `async`: Prevents parser-blocking, so the DOM is built in parallel.
    - `blocking="render"`: Holds rendering until the script executes,
      ensuring experiment changes are applied before the user sees anything.
  -->
  <script
    src="https://cdn.example.com/experiment-sdk.js"
    async
    blocking="render"
  ></script>
</head>

Đang tải kiểu dành riêng cho thử nghiệm

Nếu thử nghiệm yêu cầu một biểu định kiểu biến thể, hãy sử dụng blocking="render" trên phần tử <link>. Theo mặc định, biểu định kiểu trong <head> đã chặn việc hiển thị, nhưng biểu định kiểu được chèn động hoặc biểu định kiểu được thêm thông qua tập lệnh thì không. Sử dụng blocking="render" một cách rõ ràng khi biểu định kiểu được thêm linh động hoặc khi bạn muốn thể hiện rõ ý định.

<head>
  <!--
    DO: Use blocking="render" on experiment stylesheets that are
    dynamically inserted or conditionally loaded by the experiment SDK.
    This ensures variant styles are applied before first paint.
  -->
  <link
    rel="stylesheet"
    href="https://cdn.example.com/experiment-variant-b.css"
    blocking="render"
  >
</head>

Tập lệnh thử nghiệm cùng dòng

Nếu logic thử nghiệm đủ nhẹ để cùng dòng, hãy sử dụng tập lệnh mô-đun cùng dòng với blocking="render". Điều này rất hữu ích khi logic thử nghiệm tìm nạp một cấu hình và áp dụng trực tiếp các thay đổi về DOM.

<head>
  <!--
    DO: Use an inline module script when the experiment logic is small.
    Module scripts are deferred by default (non-parser-blocking),
    and blocking="render" ensures rendering waits for execution.
  -->
  <script type="module" blocking="render">
    // Fetch the experiment configuration from your testing platform.
    const config = await fetch('/api/experiment?id=homepage-cta')
      .then(res => res.json());

    // Apply the variant by setting a data attribute on <html>.
    // CSS rules keyed to this attribute will style the variant.
    document.documentElement.dataset.variant = config.variant;
  </script>

  <style>
    /* Default styles (control group) */
    .cta-button {
      background-color: blue;
    }

    /* Variant B styles, activated by the data attribute */
    [data-variant="b"] .cta-button {
      background-color: green;
    }
  </style>
</head>

Các phương pháp hay nhất

  • BẮT BUỘC: Tập lệnh thử nghiệm PHẢI thực thi nhanh chóng. Một tập lệnh chậm sẽ làm chậm toàn bộ quá trình kết xuất. Đặt ngân sách hiệu suất (ví dụ: thời gian thực thi dưới 100 mili giây) cho tập lệnh chặn hiển thị.
  • NÊN tách logic thử nghiệm nặng khỏi tập lệnh chặn hiển thị. Tải một mã giả lập nhỏ, chặn hiển thị để áp dụng biến thể, sau đó tải riêng các tập lệnh theo dõi hoặc phân tích nặng hơn bằng async hoặc defer (không có blocking="render").
  • NÊN sử dụng thuộc tính dữ liệu trên <html> hoặc <body> để báo hiệu biến thể đang hoạt động và sử dụng bộ chọn CSS được liên kết với thuộc tính đó để tạo kiểu cho biến thể. Điều này giúp tránh thao tác trực tiếp với DOM trong tập lệnh chặn hiển thị.
  • KHÔNG sử dụng blocking="render" trên tập lệnh phân tích, theo dõi hoặc các tập lệnh không trực quan khác. Chỉ những tập lệnh phải thay đổi nội dung mà người dùng nhìn thấy trong lần hiển thị đầu tiên mới được chặn quá trình kết xuất.
  • KHÔNG kết hợp blocking="render" với các đoạn mã chống nhấp nháy cũ. Cả hai đều giải quyết cùng một vấn đề; việc sử dụng cả hai sẽ gây ra sự chậm trễ không cần thiết.

Chiến lược dự phòng

Thuộc tính blocking không được hỗ trợ trong một số trình duyệt. Trong các trình duyệt không hỗ trợ thuộc tính này, thuộc tính sẽ bị bỏ qua và tập lệnh sẽ tải theo hành vi mặc định (async trong các ví dụ ở trên), điều này có thể dẫn đến hiện tượng nhấp nháy. Bạn cần có một phương án dự phòng để ngăn chặn hiện tượng nhấp nháy trong các trình duyệt không được hỗ trợ.

Dự phòng: Đoạn mã chống nhấp nháy

NÊN: Chỉ sử dụng một đoạn mã chống nhấp nháy gọn nhẹ làm phương án dự phòng khi không hỗ trợ blocking="render". Phát hiện tính năng hỗ trợ và bỏ qua phương án dự phòng trong các trình duyệt xử lý tính năng này theo cách gốc.

<head>
  <!--
    DO: Load the experiment script with blocking="render" for
    browsers that support it. This is the preferred approach.
  -->
  <script
    src="https://cdn.example.com/experiment-sdk.js"
    async
    blocking="render"
  ></script>

  <script>
    // DO: Only apply the anti-flicker fallback in browsers
    // that do not support blocking="render".
    if (!Object.hasOwn(HTMLScriptElement.prototype, 'blocking')) {
      // Hide the page until the experiment script runs.
      document.documentElement.classList.add('ab-loading');

      // DO: Set a timeout to reveal the page if the experiment
      // script takes too long. This prevents an indefinitely
      // blank page on slow connections. Adjust the timeout
      // to match your experiment SDK's expected load time.
      setTimeout(() => {
        document.documentElement.classList.remove('ab-loading');
      }, 4000);
    }
  </script>

  <style>
    /*
      DO: Use opacity to hide content during experiment loading.
      This is only applied when blocking="render" is unsupported.
    */
    .ab-loading {
      opacity: 0 !important;
    }
  </style>
</head>
// DO: In your experiment SDK's initialization callback,
// remove the fallback class to reveal the page.
function onExperimentReady() {
  document.documentElement.classList.remove('ab-loading');
}

Những điều cần xem xét khác

  1. Tác động đến hiệu suất: blocking="render" đánh đổi tốc độ hiển thị nội dung đầu tiên để có được độ chính xác về hình ảnh. Theo dõi Nội dung lớn nhất hiển thị (LCP) và Nội dung đầu tiên hiển thị (FCP) để đảm bảo tập lệnh thử nghiệm không gây ra độ trễ quá mức.
  2. Độ tin cậy của tập lệnh bên thứ ba: Nếu SDK thử nghiệm được lưu trữ trên CDN của bên thứ ba, thì sự cố ngừng hoạt động của CDN có thể chặn hoàn toàn quá trình kết xuất. Trình duyệt áp dụng phương pháp phỏng đoán thời gian chờ riêng (có thể dài hơn 4 giây), nhưng không có thời gian chờ do nhà phát triển kiểm soát cho blocking="render". Đảm bảo nhà cung cấp bên thứ ba có cam kết mạnh mẽ về thời gian hoạt động.
  3. Phương án thay thế phía máy chủ: Đối với các trang quan trọng về hiệu suất, hãy cân nhắc thử nghiệm A/B phía máy chủ (trong đó máy chủ trực tiếp hiển thị biến thể chính xác) thay vì thử nghiệm phía máy khách. Phương pháp phía máy chủ loại bỏ hoàn toàn hiện tượng nhấp nháy mà không tốn chi phí chặn hiển thị. Chỉ sử dụng blocking="render" phía máy khách khi không thể thử nghiệm phía máy chủ.