Processamento eficiente em segundo plano

Recursos da Web: content-visibility

Pause tarefas pesadas em segundo plano quando um componente não estiver sendo renderizado pelo navegador para conservar os recursos do sistema e a duração da bateria.

Visão geral

A propriedade content-visibility: auto permite que o navegador pule os cálculos de renderização de elementos que estão muito fora da janela de visualização. Quando o navegador decide pular ou retomar a renderização de um elemento, ele dispara o evento contentvisibilityautostatechange nesse elemento.

Ao detectar esse evento, é possível pausar operações caras, como animações <canvas>, renderização de WebGL ou sondagem de dados de WebSocket de alta frequência, quando elas não forem necessárias, e retomá-las no momento certo, quando o navegador se preparar para mostrar o conteúdo.

contentvisibilityautostatechange x IntersectionObserver

É importante entender quando usar cada API:

  • Use IntersectionObserver para a lógica do aplicativo vinculada à visibilidade exata de um elemento na janela de visualização (por exemplo, dados de carregamento lento, acionadores de rolagem infinita).
  • Use contentvisibilityautostatechange para trabalhos que exigem muita renderização (como atualizações complexas de tela ou mutações pesadas do DOM). Esse evento está diretamente ligado ao ciclo de vida de renderização interna do navegador. O navegador geralmente começa a renderizar um elemento antes que ele apareça na tela (a margem de pré-renderização). Esse evento informa quando isso acontece, garantindo que o conteúdo esteja pronto para ser visto.

Implementação

1. Aplicar visibilidade de conteúdo CSS

Defina content-visibility: auto no contêiner pesado e forneça um tamanho de marcador de posição para evitar que a barra de rolagem pule.

.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. Detectar mudanças de estado

Adicione um listener de eventos para contentvisibilityautostatechange para pausar ou retomar tarefas em 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 });

Estratégias de fallback

A propriedade content-visibility e o evento contentvisibilityautostatechange associado são aprimoramentos progressivos. Em navegadores que não são compatíveis com eles:

  • A propriedade CSS é ignorada, e o conteúdo é renderizado normalmente.
  • O evento nunca é acionado, então as tarefas em segundo plano continuam sendo executadas normalmente sem otimização.

Se você precisar oferecer suporte à pausa de tarefas em navegadores mais antigos, use IntersectionObserver como uma aproximação aproximada. Isso também ajuda a economizar bateria e CPU em dispositivos mais antigos.

// 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);
}