Vietnam

    Nodejs.vn

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Popular
    • Tags
    • Groups
    • Search

    4 cách triển khai Lazy Loading Images

    HTML/CSS
    1
    1
    51
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ?
      A Former User last edited by A Former User

      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 !

      lazy-loading.jpg

      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ư:

      • LazySizes
      • JQuery-Lazy
      • Vanilla-LazyLoad
      • Tuppola/Lazyload
      1 Reply Last reply Reply Quote 0
      • First post
        Last post
      $(document).ready(function () { app.coldLoad(); }); }