Tạm dừng các tác vụ nặng trong nền khi trình duyệt không hiển thị một thành phần để tiết kiệm tài nguyên hệ thống và thời lượng pin.
Tổng quan
Thuộc tính content-visibility: auto cho phép trình duyệt bỏ qua các phép tính kết xuất cho những phần tử nằm ngoài khung nhìn. Khi trình duyệt quyết định bỏ qua hoặc tiếp tục hiển thị một phần tử, trình duyệt sẽ kích hoạt sự kiện contentvisibilityautostatechange trên phần tử đó.
Bằng cách theo dõi sự kiện này, bạn có thể tạm dừng các thao tác tốn kém như <canvas> ảnh động, kết xuất WebGL hoặc thăm dò dữ liệu WebSocket tần suất cao khi không cần thiết, đồng thời tiếp tục các thao tác đó ngay khi trình duyệt chuẩn bị hiển thị nội dung.
contentvisibilityautostatechange đấu với IntersectionObserver
Bạn cần hiểu rõ trường hợp nào nên sử dụng API nào:
- Sử dụng
IntersectionObservercho logic ứng dụng gắn liền với khả năng hiển thị trực quan chính xác của một phần tử trong khung nhìn (ví dụ: dữ liệu tải từng phần, trình kích hoạt cuộn vô hạn). - Sử dụng
contentvisibilityautostatechangecho công việc cần nhiều tài nguyên kết xuất (chẳng hạn như cập nhật canvas phức tạp hoặc đột biến DOM nặng). Sự kiện này liên kết trực tiếp với vòng đời kết xuất nội bộ của trình duyệt. Trình duyệt thường bắt đầu hiển thị một phần tử trước khi phần tử đó thực sự xuất hiện trên màn hình (lề hiển thị trước). Sự kiện này cho bạn biết khi nào điều đó xảy ra, đảm bảo nội dung của bạn đã sẵn sàng để xem.
Triển khai
1. Áp dụng thuộc tính CSS content-visibility
Đặt content-visibility: auto trên vùng chứa nặng và cung cấp kích thước phần giữ chỗ để ngăn thanh cuộn nhảy.
.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. Lắng nghe các thay đổi về trạng thái
Thêm trình nghe sự kiện cho contentvisibilityautostatechange để tạm dừng hoặc tiếp tục các tác vụ trong nền.
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 });
Chiến lược dự phòng
Thuộc tính content-visibility và sự kiện contentvisibilityautostatechange liên quan là các cải tiến tăng dần. Trong các trình duyệt không hỗ trợ:
- Thuộc tính CSS bị bỏ qua và nội dung được hiển thị bình thường.
- Sự kiện này không bao giờ kích hoạt, vì vậy, các tác vụ trong nền sẽ tiếp tục chạy như bình thường mà không cần tối ưu hoá.
Nếu phải hỗ trợ tạm dừng tác vụ trên các trình duyệt cũ, bạn có thể quay lại sử dụng IntersectionObserver làm giá trị gần đúng. Điều này cũng giúp tiết kiệm pin và CPU trên các thiết bị cũ.
// 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);
}