高效的后台处理

Web 功能: content-visibility

当浏览器未渲染组件时,暂停繁重的后台任务,以节省系统资源并延长电池续航时间。

概览

content-visibility: auto 属性允许浏览器跳过对远离视口的元素的渲染计算。当浏览器决定跳过或恢复渲染某个元素时,它会针对该元素触发 contentvisibilityautostatechange 事件。

通过监听此事件,您可以在不需要时暂停 <canvas> 动画、WebGL 渲染或高频 WebSocket 数据轮询等高开销操作,并在浏览器准备好显示内容时及时恢复这些操作。

contentvisibilityautostatechange对比 IntersectionObserver

请务必了解何时使用哪个 API:

  • 将 IntersectionObserver 用于与视口中元素的确切视觉可见性相关的应用逻辑(例如延迟加载数据、无限滚动触发器)。
  • 使用 contentvisibilityautostatechange 执行渲染密集型工作(例如复杂的画布更新或繁重的 DOM 变更)。此事件直接与浏览器的内部渲染生命周期相关联。浏览器通常会在元素实际出现在屏幕上之前开始渲染该元素(预渲染边距)。此事件会告知您何时发生这种情况,确保您的内容已准备好供用户观看。

实现

1. 应用 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 作为粗略近似值。这也有助于在旧设备上节省电池电量和 CPU 资源。

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