4 cách triển khai Lazy Loading Images
-
Nguồn bài viết: https://dynonguyen.com/huong-dan-trien-khai-lazy-loading/
Kỹ thuật lazy loading được áp dụng rất nhiều trong thực tế, nó giúp website tải nhanh hơn, giảm băng thông cho server, từ đó tăng trải nghiệm người dùng. Bài viết này, mọi người hãy cùng mình tìm hiểu về kỹ thuật này và cách triển khai nó nhé.
Let’s get started !
Kỹ thuật Lazy Loading là gì ?
Lazy loading (Tải chậm, tải lười biếng) là một kỹ thuật được dùng để ngăn trình duyệt tải tất cả các tài nguyên của website cùng một lúc. Chỉ khi người dùng thật sự cần đến thì mới tải.
Kỹ thuật này được áp dụng cho tất cả các loại tài nguyên từ Javascript, css, nội dung, đến hình ảnh. Bài viết này mình sẽ tập trung về lazy loading images, các tài nguyên kia cũng tương tự cách làm như thế.
Ưu điểm:
-
Tốc độ tải trang nhanh hơn đáng kể.
-
Cải thiện trải nghiệm người dùng lên.
-
Tiết kiệm được rất nhiều băng thông cho hosting.
Cách 1: Bắt sự kiện scroll
Trình duyệt sẽ load bức ảnh nếu tìm thấy thuộc tính src trong thẻ img (trừ trường hợp ở cách 3) hoặc thuộc tính background-image trong css.
Vì vậy, nguyên lý cách này rất đơn giản:
*Không dùng thuộc tính src, thay vào đó dùng một thuộc tính data-src (hoặc data-gì-đó) để chứa link của image.
- Nếu không dùng thẻ img mà dùng background-image css, thì ban đầu đặt background-image: none
- Bắt sự kiện scroll của document.
- Khi bức ảnh tiến vào khung nhìn của màn hình thì ta chép thuộc tính data-src qua src (Hoặc đổi background-image: none thành url(‘src’).
- Xoá sự kiện scroll khi đã tải tất cả ảnh.
<style> .grid { max-width: 1140px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; } img { width: 100%; height: 450px; } </style> <div class="grid"> <img class="lazy-load" data-src="http://lorempixel.com/450/450" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/450/460" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/450/470" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/450/480" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/450/490" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/450/420" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/450/40" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/450/440" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/420/450" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/451/450" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/452/450" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/454/451" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/454/452" alt="Photos" /> <img class="lazy-load" data-src="http://lorempixel.com/454/453" alt="Photos" /> </div>
document.addEventListener("DOMContentLoaded", function () { let timeout = null; let lazyImgList = document.querySelectorAll('img.lazy-load'); const windowHeight = window.innerHeight; // độ cao màn hình const lazyLoadImage = () => { // áp dụng kỹ thuật debounce để giảm số lần thực thi hàm khi scroll if (timeout) { clearTimeout(timeout); } timeout = setTimeout(() => { const windowYOffset = window.pageYOffset; // vị trí scroll hiện tại lazyImgList.forEach((img, index) => { // Load ảnh khi ảnh bước vào khung nhìn màn hình // Bạn cũng có thể load nó trước khi vào view-port để tăng UX if (img.offsetTop <= windowYOffset + windowHeight) { img.src = img.dataset?.src; // Chép link ảnh qua thuộc tính src img.classList.remove('lazy-load'); } }); // Cập nhật lại list lazyImgList = document.querySelectorAll('img.lazy-load'); // Xoá event khi đã load tất cả các ảnh if (lazyImgList.length === 0) { document.removeEventListener('scroll', lazyLoadImage); return; } }, 30); } // load lần đầu nếu ảnh ở trên cùng trước khi người dùng scroll lazyLoadImage(); lazyImgList.length && document.addEventListener('scroll', lazyLoadImage); })
Tuy nhiên, chúng ta không nên dùng cách này, bạn chỉ nên dùng nó khi thật sự cần code cho các trình duyệt cũ, nếu không thì nên tham khảo các cách bên dưới nhé. Vì cách này có rất nhiều nhược điểm:
- Phụ thuộc nhiều vào JavaScript
- Xử lý scroll không tốt sẽ ảnh hưởng nhiều đến hiệu năng.
- Trang có thể bị giật lag khi tải ảnh.
Cách 2: Dùng Intersection Observer API
Intersection Observer API cung cấp các API giúp chúng ta tạo ra những giao điểm trên màn hình. Nó sẽ quan sát trình duyệt một cách bất đồng bộ, khi người dùng scroll đến giao điểm đấy thì nó sẽ chạy chức năng mà chúng ta đã định nghĩa.
Cách này cũng sẽ tối ưu hơn nhiều so với cách 1, vì chúng ta không cần phải viết, tính toán cho sự kiện scroll. Mà trình duyệt sẽ tự quan sát và thực thi.
Ứng dụng của Intersection Observer API rất nhiều, điển hình là Infinite scroll, Lazy loading.
// HTML, CSS giống ở cách 1 document.addEventListener("DOMContentLoaded", function () { const imgLazyObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { // Chép link ảnh qua src entry.target.src = entry.target.dataset?.src; // bỏ theo dõi bức ảnh này observer.unobserve(entry.target); } }); }, { // Chạy callback ở trên khi ảnh vừa vào view-port threshold: 0 }); document.querySelectorAll('img.lazy-load').forEach(img => { imgLazyObserver.observe(img) }); })
Cách 3: Dùng thuộc tính loading của thẻ img
Với cách này, bạn chỉ cần thêm thuộc tính loading="lazy" vào thẻ img là được.
Nhìn có vẻ đơn giản, tuy nhiên cách này rất hiệu quả, nhanh và gọn nhẹ. Bạn không cần phải thêm bất kỳ một dòng code javascript nào nữa.
Nếu loading="eager" thì trình duyệt sẽ tải bức ảnh đó ngay lặp tức nhé (mặc định). Tham khảo W3School.
<style> .grid { max-width: 1140px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; } img { width: 100%; height: 450px; } </style> <div class="grid"> <img src="http://lorempixel.com/350/350" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/350/360" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/350/370" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/350/380" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/350/390" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/350/320" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/350/330" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/350/340" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/320/350" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/351/350" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/352/350" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/353/350" loading="lazy" alt="Photos" /> <img src="http://lorempixel.com/354/350" loading="lazy" alt="Photos" /> </div>
Tuy nhiên, một điểm yếu của cách này là nhiều trình duyệt cho hỗ trợ (Safari chỉ mới thử nghiệm). Vì thế, nếu dùng thì bạn nên thêm polyfill cho các trình duyệt chưa support nhé. Và cách này cũng không hỗ trợ khi dùng background-image css.
Cách 4: Dùng thư viện có sẵn
Những cách trên để cho chúng ta hiểu được nguyên tắc, cách thức hoạt động của Lazy loading. Tuy nhiên, bạn có thể hoặc nên sử dụng các thư viện có sẵn để tiết kiệm thời gian và chính xác hơn nếu như logic code của bạn không quá chuyên biệt và phức tạp.
Một vài thư viện hỗ trợ việc này như:
-