Wydajne przetwarzanie w tle

Funkcje internetowe: content-visibility

Wstrzymuj obciążające zadania w tle, gdy komponent nie jest renderowany przez przeglądarkę, aby oszczędzać zasoby systemowe i baterię.

Przegląd

Właściwość content-visibility: auto umożliwia przeglądarce pomijanie obliczeń renderowania elementów, które znajdują się daleko poza widocznym obszarem. Gdy przeglądarka zdecyduje się pominąć lub wznowić renderowanie elementu, wywołuje w nim zdarzenie contentvisibilityautostatechange.

Dzięki temu zdarzeniu możesz wstrzymywać kosztowne operacje, takie jak animacje <canvas>, renderowanie WebGL czy częste odpytywanie danych WebSocket, gdy nie są potrzebne, i wznawiać je w odpowiednim momencie, gdy przeglądarka przygotowuje się do wyświetlenia treści.

contentvisibilityautostatechange a IntersectionObserver

Ważne jest, aby wiedzieć, kiedy używać którego interfejsu API:

  • Używaj IntersectionObserver do logiki aplikacji powiązanej z dokładną widocznością elementu w widocznym obszarze (np. leniwe ładowanie danych, reguły przewijania nieskończonego).
  • Używaj contentvisibilityautostatechange do zadań wymagających intensywnego renderowania (takich jak złożone aktualizacje canvas lub intensywne mutacje DOM). To zdarzenie jest bezpośrednio powiązane z wewnętrznym cyklem renderowania przeglądarki. Przeglądarka często zaczyna renderować element, zanim faktycznie pojawi się on na ekranie (margines wstępnego renderowania). To zdarzenie informuje, kiedy to nastąpi, dzięki czemu możesz mieć pewność, że Twoje treści są gotowe do wyświetlenia.

Implementacja

1. Zastosuj widoczność treści CSS

Ustaw content-visibility: auto w ciężkim kontenerze i podaj rozmiar obiektu zastępczego, aby zapobiec przeskakiwaniu paska przewijania.

.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. Nasłuchiwanie zmian stanu

Dodaj detektor zdarzeń dla zdarzenia contentvisibilityautostatechange, aby wstrzymywać lub wznawiać zadania w tle.

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

Strategie zastępcze

Właściwość content-visibility i powiązane z nią zdarzenie contentvisibilityautostatechange to progresywne ulepszanie. W przeglądarkach, które ich nie obsługują:

  • Właściwość CSS jest ignorowana, a treść jest renderowana normalnie.
  • Zdarzenie nigdy nie jest wywoływane, więc zadania w tle będą nadal działać tak, jak zwykle, bez optymalizacji.

Jeśli musisz obsługiwać wstrzymywanie zadań w starszych przeglądarkach, możesz użyć IntersectionObserver jako przybliżenia. Pomaga to też oszczędzać baterię i zasoby procesora na starszych urządzeniach.

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