El problema
Las herramientas de pruebas A/B del cliente funcionan cargando una secuencia de comandos que modifica el DOM después de que el navegador ya comenzó a construir la página. Sin intervención, el usuario ve brevemente el contenido original antes de que parpadee o destelle para mostrar la variante del experimento. Históricamente, las plataformas de pruebas han solucionado este problema con "fragmentos antiparpadeo" que ocultan toda la página con opacity: 0 hasta que finaliza la secuencia de comandos del experimento o transcurre un tiempo de espera arbitrario (por lo general, 4 segundos). Este enfoque sacrifica la renderización progresiva, permite clics accidentales en contenido invisible y genera ciclos de pintura innecesarios.
La solución
El atributo blocking=render permite que un elemento <script> o <link> ubicado en <head> bloquee la renderización, pero no el análisis, hasta que se recupere y ejecute el recurso. Esto les da a las secuencias de comandos de experimentación el mismo comportamiento de bloqueo de renderización que tienen las hojas de estilo de forma predeterminada, lo que garantiza que el navegador nunca pinte la página hasta que se haya aplicado la variante del experimento. No hay trucos de opacidad, tiempos de espera arbitrarios ni parpadeos.
Estrategia de implementación
- OBLIGATORIO: Coloca la secuencia de comandos de experimentación en el documento
<head>y agregablocking="render". - OBLIGATORIO: Asegúrate de que la secuencia de comandos tenga
type="module"(preferido para las secuencias de comandos intercaladas) oasync(preferido para las secuencias de comandos externas con un valorsrc). - SÍ mantén el guion de experimentación breve y rápido. Debido a que la renderización se bloquea hasta que se ejecuta la secuencia de comandos, una secuencia de comandos grande o lenta retrasa directamente el primer procesamiento de imagen.
- NO uses
blocking="render"en secuencias de comandos que no necesitan ejecutarse antes de la primera pintura. Está destinado solo a las secuencias de comandos cuyo resultado debe ser visible en la renderización inicial. - NO apliques
blocking="render"a las secuencias de comandos fuera de<head>. Solo las secuencias de comandos en<head>pueden bloquear la renderización.
Guía de implementación
Configuración básica
OBLIGATORIO: Carga la secuencia de comandos de experimentación con async y blocking="render". El atributo async garantiza que la secuencia de comandos no bloquee el análisis de HTML (el navegador sigue creando el DOM mientras recupera la secuencia de comandos). El atributo blocking="render" garantiza que el navegador no renderice nada hasta que se haya ejecutado la secuencia de comandos.
<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>
Carga estilos específicos del experimento
Si el experimento requiere una hoja de estilo de variante, usa blocking="render" en el elemento <link>. Las hojas de estilo en <head> ya bloquean la renderización de forma predeterminada, pero las hojas de estilo insertadas de forma dinámica o las que se agregan a través de una secuencia de comandos no lo hacen. Usa blocking="render" de forma explícita cuando la hoja de estilo se agregue de forma dinámica o cuando quieras ser explícito sobre la intención.
<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>
Secuencia de comandos de experimento intercalada
Si la lógica del experimento es lo suficientemente ligera como para intercalarla, usa una secuencia de comandos de módulo intercalada con blocking="render". Esto es útil cuando la lógica del experimento recupera una configuración y aplica cambios en el DOM directamente.
<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>
Prácticas recomendadas
- OBLIGATORIO: La secuencia de comandos de experimentación DEBE ejecutarse con rapidez. Una secuencia de comandos lenta retrasa toda la renderización. Establece un presupuesto de rendimiento (p.ej., un tiempo de ejecución inferior a 100 ms) para la secuencia de comandos que bloquea la renderización.
- SÍ separa la lógica de experimentos pesados de la secuencia de comandos que bloquea la renderización. Carga un stub pequeño que bloquee la renderización y que aplique la variante. Luego, carga secuencias de comandos de seguimiento o análisis más pesadas por separado con
asyncodefer(sinblocking="render"). - SÍ usa un atributo de datos en
<html>o<body>para indicar la variante activa y usa selectores CSS vinculados a ese atributo para el diseño de la variante. Esto evita la manipulación directa del DOM en la secuencia de comandos que bloquea la renderización. - NO uses
blocking="render"en secuencias de comandos de estadísticas, seguimiento o de otro tipo que no sean visuales. Solo las secuencias de comandos que deben cambiar lo que el usuario ve en el primer procesamiento de imagen deben bloquear la renderización. - NO combines
blocking="render"con fragmentos antiparpadeo heredados. Ambos resuelven el mismo problema; usar ambos crea retrasos innecesarios.
Estrategias de resguardo
El atributo blocking no es compatible con todos los navegadores. En los navegadores que no lo admiten, se ignora el atributo y la secuencia de comandos se carga con su comportamiento predeterminado (async en los ejemplos anteriores), lo que puede provocar parpadeos. Se requiere un resguardo para evitar el parpadeo en los navegadores no compatibles.
Resguardo: Fragmento antiparpadeo
SÍ: Usa un fragmento antiparpadeo ligero como resguardo solo cuando no se admita blocking="render". Detecta la compatibilidad con la función y omite el resguardo en los navegadores que la controlan de forma nativa.
<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');
}
Otros factores que considerar
- Impacto en el rendimiento:
blocking="render"cambia la velocidad de la primera pintura por la corrección visual. Supervisa el Largest Contentful Paint (LCP) y el First Contentful Paint (FCP) para asegurarte de que la secuencia de comandos de experimentación no agregue un retraso excesivo. - Confiabilidad de las secuencias de comandos de terceros: Si el SDK del experimento se aloja en una CDN de terceros, una interrupción de la CDN podría bloquear la renderización por completo. El navegador aplica su propia heurística de tiempo de espera (que puede ser superior a 4 segundos), pero no hay un tiempo de espera controlado por el desarrollador para
blocking="render". Asegúrate de que el proveedor externo tenga garantías sólidas de tiempo de actividad. - Alternativas del servidor: En el caso de las páginas en las que el rendimiento es fundamental, considera realizar pruebas A/B del servidor (en las que el servidor renderiza la variante correcta directamente) en lugar de pruebas del cliente. Los enfoques del servidor eliminan por completo el parpadeo sin ningún costo de bloqueo de renderización. Usa la
blocking="render"del cliente solo cuando no sea posible realizar pruebas del servidor.