Эффективная фоновая обработка

Веб-функции: content-visibility

Приостанавливайте ресурсоемкие фоновые задачи, когда браузер не обрабатывает компонент. Это позволит экономить системные ресурсы и заряд батареи.

Обзор

Свойство content-visibility: auto позволяет браузеру пропускать расчеты рендеринга для элементов, которые находятся далеко за пределами области просмотра. Когда браузер решает пропустить или возобновить отрисовку элемента, он запускает для этого элемента событие contentvisibilityautostatechange.

Отслеживая это событие, вы можете приостанавливать ресурсоемкие операции, такие как анимация <canvas>, рендеринг WebGL или высокочастотный опрос данных WebSocket, когда они не нужны, и возобновлять их непосредственно перед тем, как браузер подготовится к показу контента.

contentvisibilityautostatechange – IntersectionObserver

Важно понимать, когда какой API использовать:

  • Используйте IntersectionObserver для логики приложения, связанной с точной видимостью элемента в области просмотра (например, для триггеров отложенной загрузки данных или бесконечной прокрутки).
  • Используйте contentvisibilityautostatechange для задач, требующих интенсивного рендеринга (например, сложных обновлений холста или тяжелых изменений DOM). Это событие напрямую связано с внутренним жизненным циклом рендеринга браузера. Браузер часто начинает отрисовку элемента до того, как он появляется на экране (поле предварительной отрисовки). Это событие сообщает, когда это происходит, чтобы вы могли убедиться, что ваш контент готов к просмотру.

Реализация

1. Примените свойство content-visibility в CSS

Задайте для тяжелого контейнера свойство content-visibility: auto и укажите размер плейсхолдера, чтобы предотвратить скачки полосы прокрутки.

.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. Отслеживание изменений статуса

Добавьте прослушиватель событий для contentvisibilityautostatechange, чтобы приостанавливать или возобновлять фоновые задачи.

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

Резервные стратегии

Свойство content-visibility и связанное с ним событие contentvisibilityautostatechange – это прогрессивные улучшения. В браузерах, которые не поддерживают эти функции:

  • Свойство CSS игнорируется, и контент отображается обычным образом.
  • Событие не активируется, поэтому фоновые задачи продолжают выполняться в обычном режиме без оптимизации.

Если вам нужно поддерживать приостановку задач в старых браузерах, вы можете использовать IntersectionObserver в качестве приблизительного решения. Это также помогает экономить заряд батареи и ресурсы ЦП на старых устройствах.

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