Optymalizacja JavaScriptu innej firmy

Skrypty innych firm wpływają na ich skuteczność, dlatego warto regularnie je sprawdzać i stosować efektywne techniki ich wczytywania. Z tego ćwiczenia w programie dowiesz się, jak zoptymalizować wczytywanie zasobów zewnętrznych. Omawiamy w nim te techniki:

  • Wstrzymuję wczytywanie skryptu

  • Leniwe ładowanie zasobów niekrytycznych

  • Wstępne łączenie z wymaganymi źródłami

Dołączona przykładowa aplikacja zawiera prostą stronę internetową z 3 funkcjami pochodzącymi ze źródeł zewnętrznych:

  • Umieszczenie filmu

  • Biblioteka wizualizacji danych służąca do renderowania wykresu liniowego

  • Widżet do udostępniania treści w mediach społecznościowych

Zrzut ekranu strony z wyróżnionymi zasobami innych firm.
Zasoby innych firm w przykładowej aplikacji.

Zaczniesz od pomiaru skuteczności aplikacji, a następnie zastosujesz każdą technikę do poprawy jej różnych aspektów.

Mierz wyniki

Najpierw otwórz przykładową aplikację w widoku pełnoekranowym:

  1. Aby umożliwić edytowanie projektu, kliknij Zremiksuj do edycji.
  2. Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację, a potem Pełny ekran pełny ekran.

Przeprowadź kontrolę wydajności Lighthouse, aby ustalić poziom podstawowy:

  1. Naciśnij „Control + Shift + J” (lub „Command + Option + J” na Macu), aby otworzyć Narzędzia deweloperskie.
  2. Kliknij kartę Lighthouse.
  3. Kliknij Urządzenia mobilne.
  4. Zaznacz pole wyboru Wydajność. (Możesz wyczyścić pozostałe pola wyboru w sekcji Audyty).
  5. Kliknij Symuld Fast 3G, 4x CPU Spowolnienie.
  6. Zaznacz pole wyboru Wyczyść pamięć wewnętrzną.
  7. Kliknij Przeprowadź kontrole.

Po przeprowadzeniu kontroli na komputerze dokładne wyniki mogą się różnić, ale warto zauważyć, że czas Pierwsze wyrenderowanie treści (FCP) jest dosyć długi i Lighthouse sugeruje 2 możliwości jego zbadania: Wyeliminuj zasoby blokujące renderowanie i Wstępne łączenie z wymaganymi źródłami. (Nawet jeśli wszystkie dane są zaznaczone na zielono, optymalizacje nadal mogą przynieść ulepszenia).

Zrzut ekranu z audytu Lighthouse, który pokazuje 2,4 sekundy FCP i 2 możliwości: wyeliminuj zasoby blokujące renderowanie i wstępne łączenie z wymaganymi źródłami.

Odrocz kod JavaScript innej firmy

Audyt Wyeliminuj zasoby blokujące renderowanie wykazuje, że możesz zaoszczędzić czas, odroczając działanie skryptu pochodzącego z d3js.org:

Zrzut ekranu pokazujący kontrolę zasobów blokujących renderowanie z zaznaczonym skryptem d3.v3.min.js.

D3.js to biblioteka JavaScript do tworzenia wizualizacji danych. Plik script.js w przykładowej aplikacji używa funkcji narzędziowych D3 do utworzenia wykresu liniowego SVG i dołączenia go do strony. Kolejność działań w tym przypadku ma znaczenie: script.js musi zostać uruchomiony po przeanalizowaniu dokumentu i wczytaniu biblioteki D3, dlatego znajduje się w elemencie index.html tuż przed zamykającym tagiem </body>.

Skrypt D3 znajduje się jednak w sekcji <head> strony, co blokuje analizę pozostałej części dokumentu:

Zrzut ekranu strony index.html z wyróżnionym tagiem skryptu w nagłówku.

Po dodaniu do tagu skryptu 2 magiczne atrybuty mogą odblokować parser:

  • async gwarantuje, że skrypty będą pobierane w tle i uruchamiane przy pierwszej możliwości po zakończeniu pobierania.

  • defer gwarantuje, że skrypty są pobierane w tle i wykonywane po zakończeniu analizy.

Ten wykres nie ma kluczowego znaczenia dla całej strony i najprawdopodobniej będzie w części strony widocznej po przewinięciu, więc użyj defer, aby upewnić się, że nie ma blokowania parsera.

Krok 1. Wczytaj skrypt asynchronicznie za pomocą atrybutu defer

W wierszu 17 w tekście index.html dodaj atrybut defer do elementu <script>:

<script src="https://d3js.org/d3.v3.min.js" defer></script>

Krok 2. Sprawdź, czy kolejność działań jest prawidłowa

Ponieważ etap 3 jest odroczony, script.js uruchomi się przed przygotowaniem etapu 3, co spowoduje błąd.

Skrypty z atrybutem defer są uruchamiane w kolejności, w jakiej zostały określone. Aby mieć pewność, że script.js zostanie wykonany po przygotowaniu sesji D3, dodaj do niego tag defer i przenieś go w górę do <head> dokumentu, zaraz po elemencie <script> D3. Teraz nie blokuje już parsera, a pobieranie rozpoczyna się wcześniej.

<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

Leniwe ładowanie zasobów zewnętrznych

Wszystkie zasoby znajdujące się w części strony widocznej po przewinięciu nadają się do leniwego ładowania.

Przykładowa aplikacja zawiera film z YouTube umieszczony w elemencie iframe. Aby sprawdzić, ile żądań wysyła strona i które pochodzą z umieszczonego elementu iframe YouTube:

  1. Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację, a potem Pełny ekran pełny ekran.
  2. Naciśnij „Control + Shift + J” (lub „Command + Option + J” na Macu), aby otworzyć Narzędzia deweloperskie.
  3. Kliknij kartę Sieć.
  4. Zaznacz pole wyboru Wyłącz pamięć podręczną.
  5. W menu Ograniczanie wybierz Szybka sieć 3G.
  6. Odśwież stronę.

Zrzut ekranu przedstawiający panel Network (Sieć) w Narzędziach deweloperskich.

Z panelu Sieć wynika, że strona wysłała łącznie 28 żądań i przesłała prawie 1 MB skompresowanych zasobów.

Aby zidentyfikować żądania wysłane przez aplikację iframe w YouTube, znajdź identyfikator filmu 6lfaiXM6waw w kolumnie Inicjator. Aby zgrupować wszystkie żądania według domeny:

  • W panelu Sieć kliknij tytuł kolumny prawym przyciskiem myszy.

  • W menu wybierz kolumnę Domains (Domeny).

  • Aby posortować żądania według domeny, kliknij tytuł kolumny Domains (Domeny).

Nowy sposób sortowania pokazuje, że do domen Google są wysyłane dodatkowe żądania. Łącznie element iframe YouTube wysyła 14 żądań skryptów, arkuszy stylów, obrazów i czcionek. Jednak dopóki użytkownik nie przewinie strony, aby odtworzyć film, tak naprawdę nie potrzebuje tych wszystkich zasobów.

Poczekaj, aż użytkownik przewinie film w dół do tej sekcji strony, co powoduje zmniejszenie liczby żądań wysyłanych przez tę stronę na początku. To rozwiązanie pozwala zaoszczędzić dane użytkowników i przyspieszyć wstępne wczytywanie.

Jednym ze sposobów na wdrożenie leniwego ładowania jest użycie Intersection Observer – interfejsu API przeglądarki, który powiadomi Cię, gdy element znajdzie się w widocznym obszarze przeglądarki lub go opuści.

Krok 1. Zablokuj wstępne wczytywanie filmu

Aby leniwe ładować element iframe wideo, najpierw musisz uniemożliwić jego ładowanie w zwykły sposób. W tym celu zastąp atrybut src atrybutem data-src, aby określić adres URL filmu:

<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

data-src to atrybut danych, który umożliwia przechowywanie dodatkowych informacji o standardowych elementach HTML. Atrybut danych może mieć dowolną nazwę, jeśli tylko zaczyna się od „data-”.

Element iframe bez elementu src po prostu się nie wczyta.

Krok 2. Użyj obserwatora Intersection Observer, aby leniwie wczytywać film

Aby film został wczytany, gdy użytkownik przewinie stronę, musisz wiedzieć, kiedy to nastąpi. Właśnie tu do akcji wkracza interfejs Intersection Observer API. Interfejs Intersection Observer API umożliwia zarejestrowanie funkcji wywołania zwrotnego, która jest wykonywana za każdym razem, gdy element, który chcesz śledzić, pojawia się w widocznym obszarze lub go opuszcza.

Na początek utwórz nowy plik i nazwij go lazy-load.js:

  • Kliknij Nowy plik i nadaj mu nazwę.
  • Kliknij Dodaj ten plik.

Dodaj tag skryptu do nagłówka dokumentu:

 <script src="/lazy-load.js" defer></script>

W narzędziu lazy-load.js utwórz nowy element IntersectionObserver i prześlij do niego funkcję wywołania zwrotnego, aby ją uruchomić:

// create a new Intersection Observer
let observer = new IntersectionObserver(callback);

Teraz nadaj observer element docelowy do obejrzenia (w tym przypadku element iframe wideo), przekazując go jako argument w metodzie observe:

// the element that you want to watch
const element = document.querySelector('iframe');

// register the element with the observe method
observer.observe(element);

callback otrzymuje listę obiektów IntersectionObserverEntry i samego obiektu IntersectionObserver. Każdy wpis zawiera element target i właściwości, które opisują jego wymiary, pozycję, czas wejścia do widocznego obszaru i inne informacje. Jedną z właściwości IntersectionObserverEntry jest isIntersecting – wartość logiczna, która jest równa true, gdy element znajdzie się w widocznym obszarze.

W tym przykładzie target to iframe. Wartość isIntersecting jest równa true, gdy target znajdzie się w widocznym obszarze. Aby zobaczyć, jak to wygląda w praktyce, zastąp callback tą funkcją:

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację, a potem Pełny ekran pełny ekran.
  2. Naciśnij „Control + Shift + J” (lub „Command + Option + J” na Macu), aby otworzyć Narzędzia deweloperskie.
  3. Kliknij kartę Konsola.

Spróbuj przewinąć ekran w górę i w dół. Powinna być widoczna wartość zmiany isIntersecting i element docelowy zarejestrowany w konsoli.

Aby wczytać film, gdy użytkownik przewinie stronę w odpowiednie miejsce, użyj warunku isIntersecting. Użyj w tym celu funkcji loadElement, która pobiera wartość z parametru data-src elementu iframe i ustawia ją jako atrybut src elementu iframe. To spowoduje wczytanie filmu. Następnie po wczytaniu filmu wywołaj metodę unobserve w elemencie observer, aby zatrzymać oglądanie elementu docelowego:

let observer = new IntersectionObserver(function (entries, observer) {
  entries.forEach(entry => {
    console.log(entry.target);
    console.log(entry.isIntersecting);
  });
});
    if (entry.isIntersecting) {
      // do this when the element enters the viewport
      loadElement(entry.target);
      // stop watching
      observer.unobserve(entry.target);
    }
  });
});

function loadElement(element) {
  const src = element.getAttribute('data-src');
  element.src = src;
}

Krok 3. Ponownie oceń skuteczność

Aby zobaczyć, jak zmieniły się rozmiar i liczba zasobów, otwórz panel Sieć w Narzędziach deweloperskich i ponownie załaduj stronę. W panelu Sieć widać, że strona przesłała 14 żądań i ma tylko 260 KB. To znacząca poprawa.

Przewiń stronę w dół i zwróć uwagę na panel Sieć. Gdy dojdziesz do filmu, strona powinna wywoływać dodatkowe żądania.

Wcześniej połącz się z wymaganymi źródłami

Nastąpiło odłożenie niekrytycznego kodu JavaScript i leniwe ładowanie żądań z YouTube, czas więc zoptymalizować pozostałe treści należące do innych podmiotów.

Dodanie atrybutu rel=preconnect do linku informuje przeglądarkę, że ma nawiązać połączenie z domeną przed wysłaniem żądania dotyczącego tego zasobu. Ten atrybut najlepiej sprawdza się w przypadku źródeł, które zawierają zasoby, których dana strona potrzebuje.

Inspekcja Lighthouse przeprowadzona w pierwszym kroku zaproponowanym w sekcji Wstępnie połącz z wymaganymi źródłami. Możesz zaoszczędzić około 400 ms, nawiązując wczesne połączenia z witrynami staticxx.facebook.com i youtube.com:

Wcześniej połącz się z wymaganą kontrolą źródeł z wyróżnioną domeną staticxx.facebook.com.

Ponieważ film w YouTube jest teraz leniwie ładowany, zostaje jedynie staticxx.facebook.com, czyli źródło widżetu udostępniania w mediach społecznościowych. Aby nawiązać wczesne połączenie z tą domeną, wystarczy dodać tag <link> do sekcji <head> dokumentu:

  <link rel="preconnect" href="https://staticxx.facebook.com">

Oceń ponownie skuteczność

Oto stan strony po optymalizacji. Wykonaj czynności z sekcji Pomiar wydajności ćwiczenia z programowania, aby przeprowadzić kolejny audyt Lighthouse.

Audyt narzędzia Lighthouse z 1-sekundową wartością FCP i wynikiem wydajności na poziomie 99.