Optymalizacja JavaScriptu innej firmy

Skrypty innych firm wpływają na wydajność, dlatego ważne jest, aby regularnie je sprawdzać i stosować skuteczne techniki ich wczytywania. Z tego Codelab dowiesz się, jak zoptymalizować wczytywanie zasobów innych firm. Obejmuje ona te techniki:

  • Opóźnianie wczytywania skryptu

  • Leniwe ładowanie zasobów niekrytycznych

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

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

  • Wstawiony film

  • Biblioteka wizualizacji danych do renderowania wykresu liniowego

  • Widget udostępniania w mediach społecznościowych

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

Najpierw zmierz skuteczność aplikacji, a potem zastosuj poszczególne techniki, aby poprawić różne aspekty jej działania.

Pomiar wyników

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

  1. Kliknij Remixuj do edycji, aby umożliwić edycję projektu.
  2. Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację. Następnie kliknij Pełny ekran pełny ekran.

Przeprowadź audyt wydajności Lighthouse na stronie, aby ustalić wydajność bazową:

  1. Aby otworzyć Narzędzia dla programistów, naciśnij `Control+Shift+J` (lub `Command+Option+J` na Macu).
  2. Kliknij kartę Lighthouse.
  3. Kliknij Mobilne.
  4. Zaznacz pole wyboru Wydajność. (możesz odznaczyć pozostałe pola w sekcji Audyt).
  5. Kliknij Symulowana szybka sieć 3G, 4-krotne spowolnienie CPU.
  6. Zaznacz pole wyboru Wyczyść pamięć podręczną.
  7. Kliknij Uruchom audyty.

Gdy przeprowadzisz audyt na swoim komputerze, dokładne wyniki mogą się różnić, ale powinieneś zauważyć, że czas pierwszego wyrenderowania treści (FCP) jest dość długi, a Lighthouse sugeruje 2 możliwości, które warto sprawdzić: usuń zasoby blokujące renderowanienawiązanie wstępnego połączenia z wymaganymi źródłami. (nawet jeśli wszystkie dane są zielone, optymalizacje nadal przynoszą poprawę).

Zrzut ekranu z audytem Lighthouse pokazujący FCP wynoszące 2,4 sekundy i 2 możliwości: wyeliminowanie zasobów blokujących renderowanie oraz wstępne połączenie z wymaganymi źródłami.

Opóźnianie kodu JavaScript firm zewnętrznych

Podczas kontroli Eliminowanie zasobów blokujących renderowanie stwierdziliśmy, że możesz zaoszczędzić trochę czasu, opóźniając skrypt pochodzący z d3js.org:

Zrzut ekranu z audytem wyeliminowania zasobów blokujących renderowanie z wyróżnionym skryptem d3.v3.min.js

D3.js to biblioteka JavaScript do tworzenia wizualizacji danych. Plik script.js w próbnej aplikacji używa funkcji pomocniczych D3 do tworzenia wykresu liniowego SVG i dodawania go do strony. Ważna jest kolejność operacji: funkcja script.js musi zostać wykonana po przeanalizowaniu dokumentu i wczytaniu biblioteki D3, dlatego jest uwzględniona tuż przed zamykającym tagiem </body> w funkcji index.html.

Skrypt D3 jest jednak uwzględniony w sekcji <head> strony, co blokuje analizę reszty dokumentu:

Zrzut ekranu pliku index.html z wyróżnionym tagiem skryptu w sekcji nagłówka.

Dodanie do tagu skryptu 2 magicznych atrybutów może odblokować parsowanie:

  • async zapewnia, że skrypty są pobierane w tle i wykonywane przy pierwszej okazji po zakończeniu pobierania.

  • defer sprawia, że skrypty są pobierane w tle i uruchamiane po zakończeniu parsowania.

Ten wykres nie jest tak naprawdę kluczowy dla całej strony i najprawdopodobniej będzie widoczny poniżej załamu, więc użyj defer, aby sprawdzić, czy nie ma blokowania przez parsowanie.

Krok 1. Załaduj skrypt asynchronicznie za pomocą atrybutu defer

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

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

Krok 2. Sprawdź, czy operacje są wykonywane we właściwej kolejności

Ponieważ D3 jest opóźnione, funkcja script.js zostanie wykonana, zanim D3 będzie gotowe, co spowoduje błąd.

Skrypty z atrybutem defer są wykonywane w kolejności, w jakiej zostały określone. Aby mieć pewność, że element script.js zostanie wykonany po zakończeniu etapu D3, dodaj do niego element defer i przesuń go do <head> dokumentu, tuż po elemencie <script> etapu D3. Teraz nie blokuje już parsowania i 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 z innych witryn

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

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

  1. Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację. Następnie kliknij Pełny ekran pełny ekran.
  2. Aby otworzyć Narzędzia dla programistów, naciśnij `Control+Shift+J` (lub `Command+Option+J` na Macu).
  3. Kliknij kartę Sieć.
  4. Zaznacz pole wyboru Disable cache (Wyłącz pamięć podręczną).
  5. W menu Ograniczanie wybierz Szybki 3G.
  6. Odśwież stronę.

Zrzut ekranu przedstawiający panel Sieć w Narzędziach deweloperskich

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

Aby zidentyfikować żądania wysłane przez YouTube iframe, poszukaj identyfikatora filmu 6lfaiXM6waw w kolumnie Initiator (Inicjator). Aby pogrupować wszystkie prośby według domeny:

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

  • W menu wybierz kolumnę Domeny.

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

Nowe sortowanie ujawnia, że do domen Google wysyłane są dodatkowe żądania. W ogóle element iframe YouTube wysyła 14 żądań skryptów, arkuszy stylów, obrazów i czcionek. Jeśli jednak użytkownicy nie przewiną strony w dół, aby odtworzyć film, nie będą potrzebować wszystkich tych zasobów.

Odczekanie z leniwym wczytaniem filmu do momentu, gdy użytkownik przewinie stronę w dół, zmniejsza liczbę żądań wysyłanych przez stronę na początku. Dzięki temu podejściu oszczędzasz dane użytkowników i przyspieszasz wczytywanie.

Jednym ze sposobów implementacji wolnego wczytywania jest użycie Intersection Observer, czyli interfejsu API przeglądarki, który powiadamia, gdy element wchodzi do widocznego obszaru lub z niego wychodzi.

Krok 1. Zapobieganie wczytywaniu filmu na początku

Aby wczytywać iframe filmu z opóźnieniem, musisz najpierw uniemożliwić jego wczytywanie w zwykły sposób. Aby to zrobić, zastąp atrybut src atrybutem data-src, aby podać 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 w standardowych elementach HTML. Atrybut danych może mieć dowolną nazwę, o ile zaczyna się od „data-”.

Element iframe bez atrybutu src się nie wczyta.

Krok 2. Użyj Intersection Observer do leniwego wczytywania filmu

Aby wczytać film, gdy użytkownik przewinie do niego, musisz wiedzieć, kiedy to nastąpi. Właśnie w tym miejscu przydaje się interfejs Intersection Observer API. Interfejs Intersection Observer API umożliwia zarejestrowanie funkcji wywołania zwrotnego, która jest wykonywana, gdy element, który chcesz śledzić, wchodzi do widoku lub z niego wychodzi.

Aby rozpocząć, utwórz nowy plik o nazwie 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 pliku lazy-load.js utwórz nową funkcję IntersectionObserver i przekaż jej funkcję wywołania zwrotnego do wykonania:

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

Teraz podaj observer element docelowy do obejrzenia (w tym przypadku iframe filmu), 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 sam obiekt IntersectionObserver. Każdy wpis zawiera element target oraz właściwości opisujące jego wymiary, położenie, czas wejścia do widoku i inne informacje. Jedną z właściwości IntersectionObserverEntry jest isIntersecting – wartość logiczna, która jest równa true, gdy element wchodzi do widocznego obszaru.

W tym przykładzie target to iframe. isIntersecting jest równe true, gdy target znajdzie się w widocznym obszarze. Aby zobaczyć to w akcji, 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ę. Następnie kliknij Pełny ekran pełny ekran.
  2. Aby otworzyć Narzędzia dla programistów, naciśnij `Control+Shift+J` (lub `Command+Option+J` na Macu).
  3. Kliknij kartę Konsola.

Spróbuj przewinąć w górę i w dół. Wartość isIntersecting powinna się zmienić, a element docelowy powinien zostać zarejestrowany w konsoli.

Aby załadować film, gdy użytkownik przewinie do jego pozycji, użyj wyrażenia isIntersecting jako warunku do uruchomienia funkcji loadElement, która pobiera wartość z elementu iframe data-src i ustawia ją jako atrybut src elementu iframe. Ta wymiana powoduje wczytanie filmu. Następnie, gdy film zostanie załadowany, wywołaj metodę unobserve elementu observer, aby zatrzymać odtwarzanie 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 sprawdzić, jak zmienił się rozmiar i liczba zasobów, otwórz panel Sieć w Narzędziach deweloperskich i ponownie załaduj stronę. Z panelu Sieć wynika, że strona wysłała 14 żądań i wygenerowała tylko 260 KB. To znaczna poprawa.

Przewiń stronę w dół i obserwuj panel Sieć. Gdy przejdziesz do filmu, zobaczysz, że strona powoduje wysyłanie dodatkowych żądań.

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

Opóźniłeś/opóźniłaś wczytywanie niekrytycznych skryptów JavaScript i wykorzystałeś/wykorzystałaś opóźnione wczytywanie żądań do YouTube, więc teraz nadszedł czas na optymalizację pozostałych treści innych firm.

Dodanie atrybutu rel=preconnect do linku powoduje, że przeglądarka nawiązuje połączenie z domeną przed wysłaniem żądania dotyczącego tego zasobu. Ten atrybut najlepiej używać w przypadku źródeł, które udostępniają zasoby, których strona na pewno potrzebuje.

W ramach pierwszego kroku przeprowadzony przez Ciebie audyt Lighthouse sugeruje, że w celu wstępnego połączenia się 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:

Sprawdzanie wstępnego połączenia z wymaganymi źródłami z wyróżnioną domeną staticxx.facebook.com.

Ponieważ film w YouTube jest teraz ładowany z opóźnieniem, pozostaje tylko staticxx.facebook.com, czyli źródło widżetu udostępniania w mediach społecznościowych. Utworzenie wstępnego połączenia z tą domeną jest tak proste, jak dodanie tagu <link> do <head> w dokumencie:

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

Ponowna ocena skuteczności

Oto stan strony po optymalizacji. Aby przeprowadzić kolejny audyt Lighthouse, wykonaj czynności podane w sekcji Pomiar wydajności w codelab.

Audyt Lighthouse pokazujący czas FCP wynoszący 1 s i wynik wydajności równy 99.