ปัญหา
เครื่องมือทดสอบ 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"กับข้อมูลโค้ดป้องกันหน้าเว็บสั่นแบบเดิม เนื่องจากทั้ง 2 วิธีแก้ปัญหาเดียวกัน การใช้ทั้ง 2 วิธีจะทำให้เกิดความล่าช้าโดยไม่จำเป็น
กลยุทธ์สำรอง
บางเบราว์เซอร์ไม่รองรับแอตทริบิวต์ 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"แลกความเร็วในการแสดงผลครั้งแรกกับความถูกต้องของภาพ ตรวจสอบ Largest Contentful Paint (LCP) และ First Contentful Paint (FCP) เพื่อให้แน่ใจว่าสคริปต์การทดสอบไม่ได้เพิ่มความล่าช้ามากเกินไป - ความน่าเชื่อถือของสคริปต์ของบุคคลที่สาม: หาก SDK การทดสอบโฮสต์อยู่ใน CDN ของบุคคลที่สาม การหยุดทำงานของ CDN อาจบล็อกการแสดงผลทั้งหมด เบราว์เซอร์จะใช้การวิเคราะห์การหมดเวลาของตนเอง (ซึ่งอาจนานกว่า 4 วินาที) แต่ไม่มีการหมดเวลาที่ควบคุมโดยนักพัฒนาซอฟต์แวร์สำหรับ
blocking="render"ตรวจสอบว่าผู้ให้บริการบุคคลที่สามมีการรับประกันระยะเวลาทำงานที่เข้มงวด - ทางเลือกฝั่งเซิร์ฟเวอร์: สำหรับหน้าเว็บที่ประสิทธิภาพเป็นสิ่งสำคัญ ให้พิจารณาการทดสอบ A/B ฝั่งเซิร์ฟเวอร์ (ซึ่งเซิร์ฟเวอร์จะแสดงผลตัวแปรที่ถูกต้องโดยตรง) แทนการทดสอบฝั่งไคลเอ็นต์ แนวทางฝั่งเซิร์ฟเวอร์จะช่วยขจัดปัญหาการกะพริบได้อย่างสมบูรณ์โดยไม่มีค่าใช้จ่ายในการบล็อกการแสดงผล ใช้
blocking="render"ฝั่งไคลเอ็นต์เฉพาะเมื่อไม่สามารถทำการทดสอบฝั่งเซิร์ฟเวอร์ได้