Procesamiento en segundo plano eficiente

Funciones web: content-visibility

Pausa las tareas pesadas en segundo plano cuando el navegador no renderice un componente para conservar los recursos del sistema y la duración de la batería.

Descripción general

La propiedad content-visibility: auto permite que el navegador omita los cálculos de renderización de los elementos que están muy fuera del viewport. Cuando el navegador decide omitir o reanudar la renderización de un elemento, activa el evento contentvisibilityautostatechange en ese elemento.

Si escuchas este evento, puedes pausar operaciones costosas, como animaciones de <canvas>, renderización de WebGL o sondeo de datos de WebSocket de alta frecuencia cuando no sean necesarias, y reanudarlas justo a tiempo cuando el navegador se prepare para mostrar el contenido.

contentvisibilityautostatechange contra IntersectionObserver

Es importante comprender cuándo usar cada API:

  • Usa IntersectionObserver para la lógica de la aplicación vinculada a la visibilidad visual exacta de un elemento en el viewport (p.ej., datos de carga diferida, activadores de desplazamiento infinito).
  • Usa contentvisibilityautostatechange para el trabajo que requiere mucha renderización (como actualizaciones complejas de canvas o mutaciones pesadas del DOM). Este evento se vincula directamente con el ciclo de vida de renderización interna del navegador. El navegador suele comenzar a renderizar un elemento antes de que aparezca en pantalla (el margen de renderización previa). Este evento te indica cuándo sucede esto, lo que garantiza que tu contenido esté listo para verse.

Implementación

1. Aplica la visibilidad del contenido de CSS

Establece content-visibility: auto en el contenedor pesado y proporciona un tamaño de marcador de posición para evitar que la barra de desplazamiento salte.

.heavy-component {
  /* Defer rendering work when off-screen */
  content-visibility: auto;
  
  /* Mandatory: Provide a placeholder size to prevent layouts shifts.
    - 'auto' is optional and enables the browser to remember the actual size
      once rendered. It must be paired with a <length> value to be used for
      the first render.
    - 'none' tells the browser not to apply any intrinsic width to this element.
      It can be used for either the height or the width value.
    - '500px' is the estimated height of this element. This can be any valid
      CSS <length> value. Replace it with the expected height of your
      component.
   */
  contain-intrinsic-size: auto none auto 500px;
}

2. Detecta cambios de estado

Agrega un objeto de escucha de eventos para contentvisibilityautostatechange para pausar o reanudar las tareas en segundo plano.

const component = document.querySelector('.heavy-component');

// Option 1: Direct listener (recommended)
component.addEventListener('contentvisibilityautostatechange', (event) => {
  if (event.skipped) {
    // The browser skipped rendering this content.
    // DO NOT perform heavy mutations or animation loops here.
    stopSimulation();
    pauseWebSocketPolling();
  } else {
    // The browser is about to render the content.
    // Resume your work so it is ready when visible.
    startSimulation();
    resumeWebSocketPolling();
  }
});

// Option 2: Capturing listener for event delegation
document.addEventListener('contentvisibilityautostatechange', (event) => {
  if (event.target.matches('.heavy-component')) {
    if (event.skipped) {
      stopSimulation();
    } else {
      startSimulation();
    }
  }
}, { capture: true });

Estrategias de resguardo

La propiedad content-visibility y el evento contentvisibilityautostatechange asociado son mejoras progresivas. En los navegadores que no los admiten, ocurre lo siguiente:

  • Se ignora la propiedad de CSS y el contenido se renderiza con normalidad.
  • El evento nunca se activa, por lo que las tareas en segundo plano seguirán ejecutándose como lo harían normalmente sin optimización.

Si debes admitir la pausa de tareas en navegadores más antiguos, puedes recurrir al uso de IntersectionObserver como una aproximación aproximada. Esto también ayuda a ahorrar batería y CPU en dispositivos más antiguos.

// Fallback using IntersectionObserver for older browsers
const target = document.getElementById('target-container');

// Check if content-visibility is supported
const isSupported = 'contentVisibility' in document.documentElement.style;

if (!isSupported) {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        // The element is close to the screen. Start work!
        startSimulation();
      } else {
        // The element is far away. Pause work!
        stopSimulation();
      }
    });
  }, {
    // Use rootMargin to start rendering before it hits the screen
    rootMargin: '200px'
  });

  observer.observe(target);
}