Sprawdzone metody dotyczące leniwego ładowania

Leniwe ładowanie obrazów i filmów ma wymierne i wymierne korzyści, ale nie wymaga lekkiej pracy. Jeśli popełnisz błąd, może to skutkować niezamierzonymi konsekwencjami. W związku z tym należy pamiętać o następujących kwestiach.

Zadbaj o swoją stronę

Leniwe ładowanie wszystkich zasobów multimedialnych na stronie za pomocą języka JavaScript może być kuszące, ale trzeba się oprzeć tej pokusie. Wszystko, co znajduje się w części strony widocznej na ekranie, nie powinno być leniwie ładowane. Takie zasoby powinny być uznawane za zasoby krytyczne i w związku z tym powinny być ładowane normalnie.

Leniwe ładowanie opóźnia wczytywanie zasobów, aż do momentu, gdy DOM stanie się interaktywny, po zakończeniu wczytywania skryptów i rozpoczęciu wykonywania. Takie rozwiązanie sprawdza się w przypadku obrazów w części strony widocznej po przewinięciu, ale najważniejsze zasoby w części strony widocznej na ekranie powinny być ładowane za pomocą standardowego elementu <img>, aby wyświetlały się tak szybko, jak to możliwe.

Oczywiście w dzisiejszych czasach, gdy strony są wyświetlane na wielu ekranach o różnych rozmiarach, miejsce, w którym znajduje się część strony, nie jest takie oczywiste. To, co na laptopie znajduje się w części strony widocznej na ekranie, może leżeć poniżej na urządzeniach mobilnych. Nie istnieje uniwersalne rozwiązanie, które pomoże Ci poradzić sobie z tą sytuacją. Musisz przygotować listę najważniejszych zasobów strony i wczytywać te obrazy w typowy sposób.

Możesz też nie chcieć stosować tak rygorystycznego podejścia do linii widocznej na ekranie jako progu umożliwiającego uruchomienie leniwego ładowania. Lepszym rozwiązaniem może być ustawienie strefy bufora na pewnej odległości po przewinięciu strony, tak aby obrazy zaczęły się wczytywać na długo, zanim użytkownik przewinie je do widocznego obszaru. Na przykład interfejs Intersection Observer API umożliwia określenie właściwości rootMargin w obiekcie opcji podczas tworzenia nowej instancji IntersectionObserver. Dzięki temu elementy będą miały bufor, który uruchamia leniwe ładowanie, zanim element znajdzie się w widocznym obszarze:

let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
  // lazy-loading image code goes here
}, {
  rootMargin: "0px 0px 256px 0px"
});

Jeśli wartość w polu rootMargin wygląda podobnie do wartości określonych we właściwości CSS margin, oznacza to, że właśnie tak. W tym przypadku dolny margines obserwowanego elementu (domyślnie widoczny w przeglądarce, ale można go zmienić na konkretny element za pomocą właściwości root) jest poszerzany o 256 pikseli. Oznacza to, że funkcja wywołania zwrotnego jest wykonywana, gdy element graficzny znajduje się w zasięgu 256 pikseli od widocznego obszaru, a obraz zaczyna się wczytywać, zanim użytkownik go zobaczy.

Aby uzyskać ten sam efekt w przeglądarkach, które nie obsługują Obserwacji Intersekcji, użyj kodu obsługi zdarzeń przewijania i dostosuj pole getBoundingClientRect tak, aby uwzględnić bufor.

Przesunięcie układu i obiekty zastępcze

Leniwe ładowanie multimediów może spowodować przesunięcie układu, jeśli nie są używane symbole zastępcze. Te zmiany mogą dezorientować użytkowników i aktywować kosztowne operacje układu DOM, które zużywają zasoby systemowe i przyczyniają się do zacinania. Rozważ użycie co najmniej symbolu zastępczego w jednolitym kolorze, który zajmuje te same wymiary co obraz docelowy, lub użycie technik takich jak LQIP lub SQIP, które wskazują zawartość elementu multimedialnego przed jego załadowaniem.

W przypadku tagów <img> atrybut src powinien początkowo wskazywać zmienną, dopóki jej atrybut nie zostanie zaktualizowany o końcowy adres URL obrazu. Użyj atrybutu poster w elemencie <video>, aby wskazać obraz zastępczy. Dodatkowo użyj atrybutów width i height w tagach <img> i <video>. Dzięki temu przejście z obiektów zastępczych na ostateczne obrazy nie zmieni renderowanego rozmiaru elementu po wczytaniu multimediów.

Opóźnienia dekodowania obrazu

Wczytanie dużych obrazów w języku JavaScript i umieszczenie ich w modelu DOM może związać główny wątek, powodując, że interfejs użytkownika nie odpowiada przez krótki czas podczas dekodowania. Asynchroniczne dekodowanie obrazów za pomocą metody decode przed wstawieniem ich do DOM może ograniczyć ten problem, ale uważaj: ta funkcja nie jest jeszcze dostępna wszędzie i skomplikuje logikę leniwego ładowania. Jeśli chcesz ich użyć, musisz to zrobić. Poniżej pokazujemy, jak możesz użyć atrybutu Image.decode() z zastępczą wartością:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

if ("decode" in newImage) {
  // Fancy decoding logic
  newImage.decode().then(function() {
    imageContainer.appendChild(newImage);
  });
} else {
  // Regular image load
  imageContainer.appendChild(newImage);
}

Zapoznaj się z tym linkiem do strony CodePen, aby zobaczyć, jak działa kod podobny do tego przykładowego. Jeśli większość obrazów jest stosunkowo mała, to działanie może nie być zbyt skuteczne, ale z pewnością pomoże uniknąć zacinania się podczas leniwego wczytywania dużych obrazów i wstawiania ich do DOM.

Gdy elementy się nie ładują

Czasami zasoby multimedialne nie ładują się z jakiegoś powodu i występują błędy. Kiedy może się tak zdarzyć? To zależy, ale oto jeden hipotetyczny scenariusz: stosujesz zasadę buforowania kodu HTML przez krótki czas (np. pięć minut), a użytkownik odwiedza witrynę lub pozostawia nieaktualną kartę otwartą przez dłuższy czas (np. kilka godzin) i wraca, aby odczytać jej zawartość. W pewnym momencie w tym procesie następuje ponowne wdrożenie. Podczas tego wdrożenia nazwa zasobu obrazu zmienia się w wyniku obsługi wersji opartej na haszowaniu lub jest usuwana w całości. Gdy użytkownik leniwie wczyta obraz, zasób stanie się niedostępny i przerwie się w ten sposób.

Chociaż są to stosunkowo rzadkie przypadki, być może musisz mieć plan tworzenia kopii zapasowych, jeśli leniwe ładowanie się nie powiedzie. W przypadku obrazów rozwiązanie to:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

newImage.onerror = function(){
  // Decide what to do on error
};
newImage.onload = function(){
  // Load the image
};

To, co zrobisz w przypadku błędu, zależy od Twojej aplikacji. Możesz na przykład zastąpić obszar zastępczy obrazu przyciskiem umożliwiającym użytkownikowi ponowne załadowanie obrazu albo po prostu wyświetlić komunikat o błędzie w obszarze zastępczym obrazu.

Mogą też wystąpić inne scenariusze. Niezależnie od tego, co robisz, nigdy nie jest złym pomysłem sygnalizować użytkownikowi, że wystąpił błąd, oraz dawać mu możliwość podjęcia działania, gdy coś pójdzie nie tak.

Dostępność języka JavaScript

Nie należy zakładać, że JavaScript jest zawsze dostępny. Jeśli zamierzasz używać leniwego ładowania obrazów, rozważ udostępnienie znaczników <noscript>, które będą wyświetlać obrazy w przypadku, gdy JavaScript będzie niedostępny. Najprostszy przykład kreacji zastępczej to użycie elementów <noscript> do wyświetlania obrazów, jeśli JavaScript jest wyłączony:

Jestem obrazem!

Jeśli obsługa JavaScriptu jest wyłączona, użytkownicy zobaczą zarówno obraz zastępczy, jak i obraz zawierający elementy <noscript>. Aby obejść ten problem, umieść klasę no-js w tagu <html> w ten sposób:

<html class="no-js">

Następnie umieść jeden wiersz wbudowanego skryptu w obiekcie <head> przed żądaniem arkusza stylów za pomocą tagów <link>. Powoduje on usunięcie klasy no-js z elementu <html>, jeśli włączony jest JavaScript:

<script>document.documentElement.classList.remove("no-js");</script>

Na koniec użyj kodu CSS, aby ukryć elementy z klasą leniwego, gdy JavaScript jest niedostępny:

.no-js .lazy {
  display: none;
}

Nie zapobiega to załadowaniu obrazów zastępczych, ale wynik jest bardziej pożądany. Użytkownicy z wyłączonym JavaScriptem nie mają dostępu do obrazów zastępczych. Jest to lepszy niż obiektów zastępczych i żadnej istotnej treści graficznej.