הבעיה
כלים לבדיקות A/B בצד הלקוח פועלים על ידי טעינת סקריפט שמשנה את ה-DOM אחרי שהדפדפן כבר התחיל לבנות את הדף. אם לא מתערבים, המשתמש רואה את התוכן המקורי לזמן קצר לפני שהוא מהבהב או מבזיק לגרסת הניסוי. בעבר, פלטפורמות בדיקה עקפו את הבעיה הזו באמצעות "קטעי קוד לטעינה חלקה" שמסתירים את כל הדף באמצעות opacity: 0 עד שהסקריפט של הניסוי מסתיים או עד שחולף פרק זמן שרירותי (בדרך כלל 4 שניות). הגישה הזו פוגעת בעיבוד הדרגתי, מאפשרת קליקים מקריים על תוכן בלתי נראה ומוסיפה מחזורי עיבוד מיותרים.
הפתרון
המאפיין blocking=render מאפשר לרכיב <script> או <link> שמוצב ב-<head> לחסום את העיבוד (אבל לא את הניתוח) עד שהמשאב מאוחזר ומופעל. כך, סקריפטים של ניסויים מקבלים את אותה התנהגות של חסימת רינדור שיש לגיליונות סגנונות כברירת מחדל, והדפדפן לא מציג את הדף עד שהווריאנט של הניסוי מוחל. בלי פריצות אטימות, בלי פסקי זמן שרירותיים ובלי הבהובים.
אסטרטגיית הטמעה
- חובה: מוסיפים את הסקריפט של הניסוי למסמך
<head>ומוסיפים אתblocking="render". - חובה: מוודאים שהסקריפט כולל את המאפיין
type="module"(מומלץ לסקריפטים מוטבעים) או את המאפייןasync(מומלץ לסקריפטים חיצוניים עם הערךsrc). - מומלץ שהסקריפט של הניסוי יהיה קטן ומהיר. העיבוד נחסם עד שהסקריפט מופעל, ולכן סקריפט גדול או איטי מעכב באופן ישיר את הצביעה הראשונה.
- אל תשתמשו ב-
blocking="render"בסקריפטים שלא צריכים לפעול לפני הרינדור הראשוני. הוא מיועד רק לסקריפטים שהפלט שלהם חייב להיות גלוי ברינדור הראשוני. - אל תפעילו את
blocking="render"על סקריפטים מחוץ ל-<head>. רק סקריפטים ב-<head>יכולים לחסום עיבוד.
מדריך הטמעה
הגדרה בסיסית
חובה: צריך לטעון את הסקריפט של הניסוי גם עם async וגם עם blocking="render". המאפיין async מבטיח שהסקריפט לא יחסום את ניתוח ה-HTML (הדפדפן ממשיך לבנות את ה-DOM בזמן שהוא מאחזר את הסקריפט). המאפיין blocking="render" מבטיח שהדפדפן לא יציג שום דבר עד שהסקריפט יופעל.
<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>
מתבצעת טעינה של סגנונות ספציפיים לניסוי
אם הניסוי דורש גיליון סגנונות של וריאנט, צריך להשתמש ב-blocking="render" ברכיב <link>. גיליונות סגנונות ב-<head> כבר חוסמים את העיבוד כברירת מחדל, אבל גיליונות סגנונות שמוחדרים באופן דינמי או כאלה שנוספים באמצעות סקריפט לא חוסמים את העיבוד. השתמשו ב-blocking="render" באופן מפורש כשגיליון הסגנונות נוסף באופן דינמי או כשאתם רוצים להבהיר את הכוונה.
<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>
סקריפט של ניסוי מוטמע
אם הלוגיקה של הניסוי קלה מספיק כדי להטמיע אותה, השתמשו בסקריפט של מודול מוטמע עם blocking="render". זה שימושי כשהלוגיקה של הניסוי מאחזרת הגדרה ומחילה שינויים ב-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>
שיטות מומלצות
- חובה: הסקריפט של הניסוי חייב לפעול במהירות. סקריפט איטי מעכב את כל העיבוד. מגדירים תקציב ביצועים (למשל, זמן ביצוע של פחות מ-100 אלפיות השנייה) לסקריפט שחוסם עיבוד.
- מומלץ להפריד בין לוגיקת הניסוי הכבדה לבין הסקריפט שחוסם את העיבוד. טוענים קטע קוד קטן שחוסם רינדור ומחיל את הווריאנט, ואז טוענים בנפרד סקריפטים כבדים יותר של מעקב או ניתוח באמצעות
asyncאוdefer(בליblocking="render"). - מומלץ להשתמש במאפיין נתונים ב-
<html>או ב-<body>כדי לסמן את הווריאנט הפעיל, ולהשתמש בסלקטורים ב-CSS שמוגדרים למאפיין הזה כדי לעצב את הווריאנט. כך נמנעת מניפולציה ישירה של DOM בסקריפט שחוסם את העיבוד. - אל תשתמשו ב-
blocking="render"בסקריפטים של ניתוח נתונים, מעקב או סקריפטים אחרים שאינם חזותיים. רק סקריפטים שחייבים לשנות את מה שהמשתמש רואה ברינדור הראשוני צריכים לחסום את הרינדור. - אל תשלבו את
blocking="render"עם קטעי קוד קודמים לטעינה חלקה. הם פותרים את אותה בעיה, ושימוש בשניהם יוצר עיכובים מיותרים.
שיטות חלופיות
המאפיין blocking לא נתמך בכל הדפדפנים. בדפדפנים שלא תומכים במאפיין, המערכת מתעלמת ממנו והסקריפט נטען עם פעולת ברירת המחדל שלו (async בדוגמאות שלמעלה), מה שעלול לגרום להבהוב. צריך להגדיר חלופה כדי למנוע הבהוב בדפדפנים לא נתמכים.
חלופה: קטע קוד לטעינה חלקה
מה כן לעשות: להשתמש בקטע קוד קל לטעינה חלקה כגיבוי רק כשאין תמיכה ב-blocking="render". תמיכה בזיהוי תכונות ודילוג על החלופה בדפדפנים שמטפלים בכך באופן מקורי.
<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');
}
שיקולים נוספים
- השפעה על הביצועים:
blocking="render"מחליף את מהירות הרינדור הראשוני בנכונות חזותית. כדאי לעקוב אחרי המדדים "המהירות שבה נטען רכיב התוכן הכי גדול" (LCP) ו"הצגת תוכן ראשוני" (FCP) כדי לוודא שסקריפט הניסוי לא מוסיף עיכוב משמעותי. - אמינות של סקריפטים של צד שלישי: אם ערכת ה-SDK של הניסוי מתארחת ב-CDN של צד שלישי, הפסקה זמנית בפעילות של ה-CDN עלולה לחסום את העיבוד לחלוטין. הדפדפן מפעיל היוריסטיקות משלו לזמן קצוב לתפוגה (שיכול להיות ארוך מ-4 שניות), אבל אין זמן קצוב לתפוגה בשליטת המפתח עבור
blocking="render". מוודאים שלספק הצד השלישי יש התחייבויות חזקות לזמן פעולה תקינה. - חלופות בצד השרת: בדפים שבהם הביצועים קריטיים, כדאי לשקול בדיקות A/B בצד השרת (שבהן השרת מעבד את הווריאנט הנכון ישירות) במקום בדיקות בצד הלקוח. גישות בצד השרת מבטלות לחלוטין את ההבהוב בלי עלות של חסימת רינדור. השתמשו ב-
blocking="render"בצד הלקוח רק כשאי אפשר לבצע בדיקה בצד השרת.