Leniwe ładowanie obrazów

Obrazy mogą pojawiać się na stronie internetowej, ponieważ są wbudowane w kod HTML jako elementy <img> lub obrazy tła CSS. Z tego posta dowiesz się, jak leniwie ładować oba typy obrazów.

Obrazy w treści

Najpopularniejszymi kandydatami do leniwego ładowania są obrazy używane w elementach <img>. W przypadku obrazów w treściach mamy 3 opcje leniwego ładowania, które można łączyć, aby uzyskać najlepszą zgodność w różnych przeglądarkach:

Korzystanie z leniwego ładowania na poziomie przeglądarki

Zarówno Chrome, jak i Firefox obsługują leniwe ładowanie za pomocą atrybutu loading. Ten atrybut można dodawać do elementów <img>, a także do elementów <iframe>. Wartość lazy informuje przeglądarkę, że ma natychmiast wczytać obraz, jeśli znajduje się on w widocznym obszarze, i pobrać inne obrazy, gdy użytkownik przewinie stronę w pobliżu.

Szczegółowe informacje o obsłudze przeglądarek znajdziesz w polu loading w tabeli zgodności przeglądarek w MDN. Jeśli przeglądarka nie obsługuje leniwego ładowania, atrybut zostanie zignorowany, a obrazy zostaną wczytane od razu, jak zwykle.

W przypadku większości witryn dodanie tego atrybutu do obrazów w treści pozwoli zwiększyć wydajność i zaoszczędzić użytkownikom wczytującym obrazy, do których użytkownicy mogą w ogóle nie przewinąć. Jeśli masz dużą liczbę obrazów i chcesz mieć pewność, że użytkownicy przeglądarek nie obsługują funkcji leniwego ładowania, musisz to połączyć z jedną z opisanych niżej metod.

Więcej informacji znajdziesz w artykule Leniwe ładowanie stron internetowych na poziomie przeglądarki.

Korzystanie z obserwatora skrzyżowań

Do polyfill leniwego ładowania elementów <img> używamy JavaScriptu, aby sprawdzić, czy znajdują się one w widocznym obszarze. Jeśli tak, atrybuty src (a czasem srcset) są wypełniane adresami URL wybranych treści graficznych.

Jeśli masz już kod leniwego ładowania, to zadanie mogło zostać wykonane dzięki modułom obsługi zdarzeń, takim jak scroll czy resize. Chociaż takie podejście jest najbardziej zgodne z różnymi przeglądarkami, nowoczesne przeglądarki pozwalają na sprawdzanie widoczności elementów za pomocą interfejsu Intersection Observer API.

Obserwacja intersekcji jest łatwiejsza w obsłudze i odczytywana niż kod polegający na różnych modułach obsługi zdarzeń, ponieważ wystarczy zarejestrować obserwatora, aby obserwować elementy, zamiast pisać żmudny kod wykrywania widoczności elementów. Trzeba tylko zdecydować, co zrobić, gdy dany element jest widoczny. Przyjmijmy ten podstawowy wzorzec znaczników dla leniwie ładowanych elementów <img>:

<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!">

Skup się na tych 3 istotnych elementach:

  1. Atrybut class, za pomocą którego wybierasz element w języku JavaScript.
  2. Atrybut src, który odwołuje się do obrazu zastępczego, który pojawi się po pierwszym wczytaniu strony.
  3. Atrybuty data-src i data-srcset to atrybuty zastępcze zawierające adres URL obrazu, który zostanie wczytany, gdy element znajdzie się w widocznym obszarze.

Zobaczmy teraz, jak wykorzystać Obserwatorium Intersection w języku JavaScript do leniwego ładowania obrazów z użyciem tego wzorca znaczników:

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 event handlers here
  }
});

W zdarzeniu DOMContentLoaded dokumentu ten skrypt wysyła zapytanie do DOM w przypadku wszystkich elementów <img> z klasą lazy. Jeśli masz dostęp do obserwatora Intersection Observer, utwórz nowego obserwatora, który będzie uruchamiać wywołanie zwrotne, gdy elementy img.lazy pojawią się w widocznym obszarze.

Obserwator połączeń jest dostępny we wszystkich nowoczesnych przeglądarkach. Dlatego użycie go jako kodu polyfill w loading="lazy" sprawi, że leniwe ładowanie będzie dostępne dla większości użytkowników.

Obrazy w CSS

Tagi <img> to najczęstszy sposób stosowania obrazów na stronach internetowych, ale obrazy można też wywoływać za pomocą właściwości CSS background-image (i innych właściwości). Leniwe ładowanie na poziomie przeglądarki nie ma zastosowania do obrazów tła CSS. Jeśli więc masz obrazy tła do leniwego ładowania, rozważ inne metody.

W przeciwieństwie do elementów <img>, które wczytują się niezależnie od ich widoczności, wczytywanie obrazów w CSS odbywa się z większą ilością spekulacji. Po utworzeniu modelu dokumentu i obiektu CSS oraz drzewa renderowania przeglądarka sprawdza zastosowanie kodu CSS do dokumentu, zanim zażąda zasobów zewnętrznych. Jeśli przeglądarka ustaliła, że reguła CSS obejmująca zasób zewnętrzny nie ma zastosowania do obecnie utworzonego dokumentu, przeglądarka nie wysyła tego żądania.

Takie zachowanie spekulacyjne może służyć do opóźnienia wczytywania obrazów w CSS za pomocą JavaScriptu do określania, kiedy element znajduje się w widocznym obszarze, a potem przez zastosowanie do niego klasy, która stosuje styl wywołujący obraz tła. Dzięki temu obraz jest pobierany w odpowiednim momencie, a nie przy początkowym wczytywaniu. Weźmy np. element, który zawiera duży baner powitalny:

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

Element div.lazy-background zwykle zawiera baner powitalny wywoływany przez kod CSS. W tym przykładzie leniwego ładowania można jednak wyodrębnić właściwość background-image elementu div.lazy-background za pomocą klasy visible dodanej do elementu, gdy znajduje się on w widocznym obszarze:

.lazy-background {
  background-image: url("hero-placeholder.jpg"); /* Placeholder image */
}

.lazy-background.visible {
  background-image: url("hero.jpg"); /* The final image */
}

Teraz za pomocą JavaScriptu sprawdź, czy element znajduje się w widocznym obszarze (za pomocą Obserwatora dziesiętnego!), i dodaj do elementu div.lazy-background klasę visible, która wczyta obraz:

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);
    });
  }
});

Wpływ na największe wyrenderowanie treści (LCP)

Leniwe ładowanie to świetna optymalizacja, która zmniejsza zarówno ogólne użycie danych, jak i rywalizację z siecią podczas uruchamiania, opóźniając wczytywanie obrazów do momentu, gdy są faktycznie potrzebne. Może to skrócić czas uruchamiania i zmniejszyć przetwarzanie w wątku głównym przez skrócenie czasu potrzebnego na dekodowanie obrazów.

Leniwe ładowanie to jednak technika, która może niekorzystnie wpłynąć na największy wskaźnik LCP wyrenderowania treści w Twojej witrynie, jeśli Ci się podoba. Jedną z rzeczy, których należy unikać, jest leniwe ładowanie obrazów, które znajdują się w widocznym obszarze podczas uruchamiania.

Stosując leniwe ładowanie oparte na języku JavaScript, unikaj leniwego ładowania obrazów w widocznym obszarze, ponieważ te rozwiązania często używają atrybutu data-src lub data-srcset jako symbolu zastępczego atrybutów src i srcset. Problem polega na tym, że wczytywanie tych obrazów będzie opóźnione, ponieważ skaner wstępnego wczytywania przeglądarki nie może ich znaleźć podczas uruchamiania.

Nawet leniwe ładowanie na poziomie przeglądarki w celu leniwego ładowania obrazu w widocznym obszarze może się nie udać. Jeśli zastosujesz właściwość loading="lazy" do obrazu widocznego w widocznym obszarze, wyświetlenie tego obrazu będzie opóźnione, dopóki przeglądarka nie będzie wiedzieć, czy znajduje się w widocznym obszarze, co może mieć wpływ na LCP strony.

Nigdy nie uruchamiaj leniwego ładowania obrazów, które są widoczne w widocznym obszarze podczas uruchamiania. Jest to wzorzec, który negatywnie wpłynie na LCP witryny, a co za tym idzie na wrażenia użytkowników. Jeśli chcesz mieć obraz przy uruchamianiu, ładuj go jak najszybciej, bez leniwego ładowania.

Leniwe ładowanie bibliotek

Gdy tylko jest to możliwe, używaj leniwego ładowania na poziomie przeglądarki. Jeśli jednak okaże się, że nie jest to możliwe (np. znaczna grupa użytkowników nadal wymaga starszych przeglądarek), możesz użyć tych bibliotek do leniwego ładowania obrazów:

  • lenisizes to biblioteka leniwego ładowania, która umożliwia leniwe ładowanie obrazów i elementów iframe; Używany wzorzec jest bardzo podobny do pokazanego tutaj przykładu, ponieważ automatycznie wiąże się z klasą lazyload w elementach <img> i wymaga podania adresów URL obrazów w atrybutach data-src lub data-srcset, których zawartość jest zastępowana odpowiednio atrybutami src lub srcset. Wykorzystuje on obserwację Intersection Observer (którego możesz użyć polyfill) i można go rozszerzyć za pomocą wielu wtyczek, aby umożliwić np. leniwe ładowanie filmów. Więcej informacji o korzystaniu z leniwych rozmiarów
  • vanilla-lazyload to lekka opcja leniwego ładowania obrazów, obrazów tła, filmów, elementów iframe i skryptów. Wykorzystuje on obserwację Intersection Observer, obsługuje elastyczne obrazy i umożliwia leniwe ładowanie na poziomie przeglądarki.
  • lozad.js to kolejna uproszczona opcja, która korzysta tylko z Obserwatora Intersection Observer. Jest on bardzo wydajny, ale przed użyciem w starszych przeglądarkach trzeba go wypełnić.
  • Jeśli potrzebujesz biblioteki leniwego ładowania specyficznej dla React, możesz zastosować metodę react-lazyload. Nie korzysta on z intersection Observer, ale zapewnia znaną metodę leniwego wczytywania obrazów dla osób przyzwyczajonych do tworzenia aplikacji w React.