Lazy Loading Images
Bài đăng này đã không được cập nhật trong 5 năm
Phần hình ảnh và video trong việc load dữ liệu của một trang web có thể là đáng kể. Không may, các bên liên quan dự án có thể không muốn cắt bất kỳ nội dung nào từ các ứng dụng hiện tại của họ. Những bế tắc như vậy rất bực bội, đặc biệt khi tất cả các bên liên quan muốn cải thiện hiệu suất trang web, nhưng không thể đồng ý làm thế nào để đạt được điều đó. May mắn thay, lazy load là một giải pháp làm giảm tải trang ban đầu và thời gian tải, nhưng không bỏ qua về mặt nội dung.
What is lazy loading?
Lazy loading là kỹ thuật ngăn chặn việc tải các tài nguyên không quan trọng vào thời điểm tải trang. Thay vào đó, các tài nguyên không quan trọng này được nạp vào lúc cần thiết. Ở đâu có hình ảnh, "không quan trọng" thường đồng nghĩa với "off-screen". Nếu bạn đã sử dụng Lighthouse và kiểm tra một số cơ hội để cải tiến, bạn có thể đã thấy một số hướng dẫn trong lĩnh vực này dưới hình thức Offscreen Images audit:
Hình 1: Một trong những kiểm tra hiệu suất của Lighthouse là xác định các hình ảnh ngoài màn hình, đây là những ứng cử viên cho lazy loading. Bạn đã có thể đã thấy lazy loading trong hành động, và nó đi một cái gì đó như thế này:
- Bạn đến một trang và bắt đầu cuộn khi bạn đọc nội dung.
- Tại một số điểm, bạn di chuyển một hình ảnh giữ chỗ vào khung nhìn.
- Hình ảnh trình giữ chỗ đột nhiên được thay thế bằng hình ảnh cuối cùng.
Ví dụ về lazy loading hình ảnh có thể được tìm thấy trên nền tảng phương tiện xuất bản phổ biến Medium, tải những hình ảnh giữ chỗ nhẹ ở trang tải và thay thế chúng bằng các hình ảnh lazy images được tải khi chúng cuộn vào khung nhìn.
Hình 2 Một ví dụ về hành động tải hình ảnh của lazy loading. Một hình ảnh giữ chỗ được tải ở trang tải (trái), và khi cuộn vào khung nhìn, hình ảnh cuối cùng tải vào thời điểm cần. Nếu bạn không quen với lazy loading, bạn có thể tự hỏi kỹ thuật này có hữu ích như thế nào và lợi ích của nó là gì. Đọc để tìm hiểu!
Why lazy load images instead of just loading them?
Bởi vì có thể bạn đang tải nội dung mà người dùng có thể không bao giờ nhìn thấy. Đây là vấn đề vì một vài lý do:
- Nó lãng phí dữ liệu. Trên các kết nối chưa được kiểm soát, đây không phải là điều tồi tệ nhất có thể xảy ra (mặc dù bạn có thể sử dụng băng thông quý giá này để tải các tài nguyên khác mà người dùng có thể nhìn thấy). Tuy nhiên, về kế hoạch dữ liệu hạn chế, việc tải nội dung mà người dùng không bao giờ thấy có thể là một sự lãng phí tiền của họ.
- Nó lãng phí thời gian xử lý, pin, và tài nguyên hệ thống khác. Sau khi tài nguyên truyền thông được tải xuống, trình duyệt phải giải mã nó và hiển thị nội dung của nó trong khung nhìn.
Khi sử dụng lazy loading ảnh, chúng tôi giảm thời gian tải trang ban đầu, dung lượng trang ban đầu và sử dụng tài nguyên hệ thống, tất cả đều có tác động tích cực đến hiệu suất. Trong hướng dẫn này, chúng tôi sẽ giới thiệu một số kỹ thuật và cung cấp hướng dẫn cho lazy loading hình ảnh, cũng như danh sách ngắn của một số thư viện thường sử dụng.
Lazy loading images
lazy loading hình ảnh cơ chế là đơn giản trong lý thuyết, nhưng các chi tiết thực sự là một chút vớ vẩn. Cộng thêm có một vài trường hợp sử dụng khác biệt mà cả hai đều có thể hưởng lợi từ lazy load. Đầu tiên chúng ta bắt đầu với các images lazy loading trong HTML.
Inline images
Những ứng cử viên lazy loading phổ biến nhất là những hình ảnh được sử dụng trong các phần tử <img>. Khi chúng tôi dùng lazy loading tải các yếu tố <img>, chúng tôi sử dụng JavaScript để kiểm tra xem họ đang ở chế độ xem. Nếu có, các thuộc tính src (và đôi khi srcset) của chúng được chứa các URL với nội dung hình ảnh mong muốn.
Using intersection observer Nếu bạn đã viết lazy loading trước đây, bạn có thể đã hoàn thành nhiệm vụ của mình bằng cách sử dụng trình xử lý sự kiện như di chuyển hoặc thay đổi kích cỡ. Mặc dù cách tiếp cận này là tương thích nhất trên các trình duyệt, các trình duyệt hiện đại cung cấp một cách thực hiện và hiệu quả hơn để thực hiện công việc kiểm tra tính hiển thị của phần tử thông qua the intersection observer API. Intersection observer dễ sử dụng và đọc hơn là mã dựa vào các trình xử lý sự kiện khác nhau, bởi vì các nhà phát triển chỉ cần đăng ký một observer để xem các phần tử chứ không phải là viết mã nguồn phát hiện yếu tố tẻ nhạt. Tất cả những gì còn lại để làm cho nhà phát triển là quyết định phải làm gì khi một phần tử có thể nhìn thấy được. Giả sử mẫu đánh dấu cơ bản này cho các phần tử <img> được sử dụng lazy load:
<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">
Có ba phần liên quan của đánh dấu này, chúng ta nên tập trung vào:
- Thuộc tính lớp, là cái mà chúng ta sẽ chọn phần tử bằng trong JavaScript.
- Thuộc tính src, tham chiếu đến một hình ảnh giữ chỗ sẽ xuất hiện khi trang đầu tiên tải.
- Thuộc tính data-src và data-srcset, thuộc tính giữ chỗ có chứa URL cho hình ảnh chúng ta sẽ tải khi phần tử nằm trong khung nhìn.
Bây giờ chúng ta hãy xem cách chúng ta có thể sử Intersection observer trong JavaScript để lazy load images bằng cách sử dụng mô hình đánh dấu này:
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Possibly fall back to a more compatible method here
}
});
Trên sự kiện DOMContentLoaded, tập lệnh này truy vấn DOM cho tất cả các phần tử <img> với lazy load. Nếu có Intersection observer, chúng ta sẽ tạo ra một observer mới chạy một callback khi các phần tử img.lazy nhập viewport. Xem ví dụ CodePen này để xem mã này đang hoạt động.
Hạn chế củaIntersection observer, tuy nhiên, là trong khi nó có sự hỗ trợ tốt giữa các trình duyệt, nó không phổ thông. Bạn sẽ cần nhiều trình duyệt đa chức năng không hỗ trợ nó, hoặc như mã trên gợi ý, phát hiện nếu nó có sẵn và sau đó quay trở lại các phương pháp cũ hơn, tương thích hơn.
Sử dụng trình xử lý sự kiện (cách tương thích nhất)
Trong khi bạn nên sử dụngintersection observer cho lazy loading, yêu cầu ứng dụng của bạn có thể tương thích với trình duyệt. Bạn có thể polyfill hỗ trợ quan sát giao lộ (và điều này sẽ là dễ dàng nhất), nhưng bạn cũng có thể rơi trở lại mã bằng cách sử dụng di chuyển, thay đổi kích cỡ, và có thể định hướng xử lý sự kiện trong buổi hòa nhạc với getBoundingClientRect để xác định xem một phần tử là trong viewport. Giả sử cùng một mẫu đánh dấu từ trước, sau đây JavaScript cung cấp chức năng lazy load:
document.addEventListener("DOMContentLoaded", function() {
let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
let active = false;
const lazyLoad = function() {
if (active === false) {
active = true;
setTimeout(function() {
lazyImages.forEach(function(lazyImage) {
if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("lazy");
lazyImages = lazyImages.filter(function(image) {
return image !== lazyImage;
});
if (lazyImages.length === 0) {
document.removeEventListener("scroll", lazyLoad);
window.removeEventListener("resize", lazyLoad);
window.removeEventListener("orientationchange", lazyLoad);
}
}
});
active = false;
}, 200);
}
};
document.addEventListener("scroll", lazyLoad);
window.addEventListener("resize", lazyLoad);
window.addEventListener("orientationchange", lazyLoad);
});
Mã này sử dụng getBoundingClientRect trong một trình xử lý sự kiện di chuyển để kiểm tra xem có bất kỳ yếu tố img.lazy nào trong khung nhìn. Một cuộc gọi setTimeout được sử dụng để trì hoãn quá trình xử lý, và một biến hoạt động chứa trạng thái xử lý được sử dụng để điều khiển các cuộc gọi chức năng. Khi lazy images được tải, chúng sẽ được gỡ bỏ khỏi mảng phần tử. Khi mảng phần tử đạt đến chiều dài là 0, mã trình xử lý sự kiện di chuyển sẽ bị xóa. Xem mã này đang hoạt động trong ví dụ CodePen này. Mặc dù mã này hoạt động trong hầu hết bất kỳ trình duyệt nào nhưng nó có các vấn đề hiệu năng tiềm ẩn trong các cuộc gọi setTimeout lặp đi lặp lại có thể lãng phí, ngay cả khi mã trong đó bị hạn chế. Trong ví dụ này, một kiểm tra đang được chạy mỗi 200 mili giây trên tài liệu di chuyển hoặc thay đổi kích thước cửa sổ bất kể có một hình ảnh trong khung nhìn hay không. Plus, công việc tẻ nhạt của việc theo dõi có bao nhiêu phần tử còn lại lazyload và unbinding xử lý sự kiện di chuyển được để lại cho các nhà phát triển. Đơn giản chỉ cần đặt: Sử dụngintersection observer ở bất cứ nơi nào có thể và quay trở lại các trình xử lý sự kiện nếu khả năng tương thích rộng nhất có thể là yêu cầu ứng dụng quan trọng.
Images in CSS
Mặc dù thẻ <img> là cách sử dụng hình ảnh phổ biến nhất trên các trang web, nhưng hình ảnh cũng có thể được gọi thông qua thuộc tính CSS-background-image (và các thuộc tính khác). Không giống như các phần tử <img> tải bất kể khả năng hiển thị của chúng, hành vi tải ảnh trong CSS được thực hiện với nhiều suy đoán hơn. Khi tài liệu và mô hình đối tượng CSS và cây hiển thị được xây dựng, trình duyệt kiểm tra cách CSS được áp dụng cho một tài liệu trước khi yêu cầu tài nguyên bên ngoài. Nếu trình duyệt đã xác định quy tắc CSS liên quan đến tài nguyên bên ngoài không áp dụng cho tài liệu như nó hiện đang được xây dựng, trình duyệt sẽ không yêu cầu nó. Hành vi đầu cơ này có thể được sử dụng để trì hoãn tải hình ảnh trong CSS bằng cách sử dụng JavaScript để xác định khi nào một phần tử nằm trong khung nhìn và sau đó áp dụng một lớp cho phần tử đó áp dụng kiểu dáng gọi một hình nền. Điều này làm cho hình ảnh được tải xuống tại thời điểm cần thay vì lúc tải ban đầu. Ví dụ: hãy lấy một phần tử chứa hình ảnh anh hùng lớn:
<div class="lazy-background">
<h1>Here's a hero heading to get your attention!</h1>
<p>Here's hero copy to convince you to buy a thing!</p>
<a href="/buy-a-thing">Buy a thing!</a>
</div>
Phần tử nền div.lazy thường chứa hình ảnh nền anh hùng được gọi bởi một số CSS. Tuy nhiên, trong ví dụ lazyload này, chúng ta có thể cô lập thuộc tính ảnh nền của phần tử nền div.lazy qua một lớp hiển thị mà chúng ta sẽ thêm vào phần tử khi nó ở viewport:
.lazy-background {
background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}
.lazy-background.visible {
background-image: url("hero.jpg"); /* The final image */
}
Từ đây, chúng tôi sẽ sử dụng JavaScript để kiểm tra xem phần tử đó có ở chế độ xem (với intersection observer) và thêm lớp hiển thị vào phần tử nền div.lazy vào thời điểm đó, tải hình ảnh đó:
document.addEventListener("DOMContentLoaded", function() {
var lazyBackgrounds = [].slice.call(document.querySelectorAll(".lazy-background"));
if ("IntersectionObserver" in window) {
let lazyBackgroundObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
lazyBackgroundObserver.unobserve(entry.target);
}
});
});
lazyBackgrounds.forEach(function(lazyBackground) {
lazyBackgroundObserver.observe(lazyBackground);
});
}
});
Như đã đề cập trước đó, bạn sẽ muốn chắc chắn rằng bạn cung cấp một dự phòng hoặc một polyfill cho intersection observer vì không phải tất cả các trình duyệt hiện đang hỗ trợ nó. Hãy kiểm tra bản giới thiệu CodePen này để xem mã này đang hoạt động.
All rights reserved