پردازش پس‌زمینه‌ای کارآمد

ویژگی‌های وب: content-visibility

وقتی مؤلفه‌ای توسط مرورگر رندر نمی‌شود، کارهای سنگین پس‌زمینه را موقتاً متوقف کنید تا منابع سیستم و عمر باتری حفظ شود.

نمای کلی

ویژگی content-visibility: auto به مرورگر اجازه می‌دهد محاسبات پردازشی را برای عناصری که بسیار خارج از نمای هدف هستند نادیده بگیرد. وقتی مرورگر تصمیم می‌گیرد از پردازش یک عنصر صرف‌نظر کند یا آن را ازسر بگیرد، رویداد contentvisibilityautostatechange را در آن عنصر فعال می‌کند.

با گوش دادن به این رویداد، می‌توانید عملیات پرهزینه مانند پویانمایی‌های <canvas>، رندر WebGL، یا نظرسنجی داده‌های WebSocket با فرکانس بالا را در زمانی که به آن‌ها نیاز نیست موقتاً متوقف کنید و درست به‌موقع وقتی مرورگر برای نمایش محتوا آماده می‌شود آن‌ها را ازسر بگیرید.

contentvisibilityautostatechange دربرابر IntersectionObserver

مهم است بدانید چه زمانی از کدام API استفاده کنید:

  • از IntersectionObserver برای منطق برنامه استفاده کنید که به رؤیت‌پذیری دیداری دقیق عنصر در ناحیه نمایش گره خورده است (برای مثال، داده‌های بارکردن کُند، راه‌اندازهای پیمایش بی‌پایان).
  • از contentvisibilityautostatechange برای کارهای سنگین پردازشی (مانند به‌روزرسانی‌های پیچیده بوم یا تغییرات سنگین DOM) استفاده کنید. این رویداد مستقیماً به چرخه حیات پرداز داخلی مرورگر مرتبط است. مرورگر اغلب پرداز زدن عنصر را قبل‌از اینکه واقعاً در صفحه ظاهر شود شروع می‌کند (حاشیه پیش‌پرداز). این رویداد به شما می‌گوید که این اتفاق چه زمانی رخ می‌دهد و اطمینان حاصل می‌کند که محتوای شما آماده دیده شدن است.

پیاده‌سازی

۱. اعمال کردن رؤیت‌پذیری محتوای 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;
}

۲. گوش دادن به تغییرات وضعیت

برای توقف موقت یا ازسرگیری تکالیف پس‌زمینه، شنونده رویداد برای 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);
}