Optymalizowanie ładowania skryptów zewnętrznych w Next.js

Poznaj wizję komponentu Script w Next.js, który udostępnia wbudowane rozwiązanie do optymalizacji wczytywania skryptów innych firm.

Leena Sohoni
Leena Sohoni

Około 45% żądań z witryn wyświetlanych na urządzeniach mobilnych i komputerach to żądania zewnętrzne, z czego 33% to skrypty. Rozmiar, czas oczekiwania i wczytywanie skryptów innych firm mogą mieć znaczący wpływ na wydajność witryny. Komponent skryptu Next.js zawiera wbudowane sprawdzone metody i ustawienia domyślne, które pomagają deweloperom wprowadzać skrypty innych firm do aplikacji, a jednocześnie od razu rozwiązują potencjalne problemy z wydajnością.

Skrypty innych firm i ich wpływ na wydajność

Skrypty innych firm umożliwiają programistom stron internetowych wykorzystanie istniejących rozwiązań do wdrażania typowych funkcji i skracania czasu programowania. Jednak twórcy tych skryptów zwykle nie mają motywacji, aby brać pod uwagę wpływ na wydajność witryny, która z nich korzysta. Te skrypty są też czarną skrzynką dla deweloperów, którzy ich używają.

Skrypty stanowią znaczną liczbę bajtów innych firm pobieranych przez witryny w różnych kategoriach żądań innych firm. Domyślnie przeglądarka nadaje skryptom priorytety na podstawie ich lokalizacji w dokumencie, co może opóźnić wykrywanie lub wykonywanie skryptów, które mają kluczowe znaczenie dla wygody użytkowników.

Biblioteki innych firm wymagane do układu powinny być wczytywane wcześniej, aby wyrenderować stronę. Zasoby innych firm, które nie są wymagane do wstępnego renderowania, powinny być odroczone, aby nie blokowały innych procesów w głównym wątku. Lighthouse ma 2 audyty, które oznaczają skrypty blokujące renderowanie lub główny wątek.

Audyty Lighthouse dotyczące eliminowania zasobów blokujących renderowanie i minimalizowania wykorzystania kodu zewnętrznego

Ważne jest, aby wziąć pod uwagę sekwencję ładowania zasobów strony, aby nie opóźniać ładowania zasobów krytycznych i aby zasoby niekrytyczne nie blokowały zasobów krytycznych.

Chociaż istnieją sprawdzone metody, które pozwalają ograniczyć wpływ kodu zewnętrznego, nie każdy wie, jak je wdrożyć w przypadku wszystkich używanych usług zewnętrznych. Może to być skomplikowane, ponieważ:

  • Średnio witryny korzystają z 21–23 różnych usług innych firm (w tym skryptów) na urządzeniach mobilnych i komputerach. Sposób użycia i zalecenia mogą się różnić w zależności od typu.
  • Wdrażanie wielu usług innych firm może się różnić w zależności od tego, czy używana jest konkretna platforma czy biblioteka interfejsu.
  • Często wprowadzane są nowsze biblioteki innych firm.
  • Różne wymagania biznesowe związane z tą samą usługą zewnętrzną utrudniają deweloperom standaryzację jej użycia.

Skupienie się na skryptach innych firm w projekcie Aurora

Częścią współpracy Aurora z platformami i narzędziami internetowymi typu open source jest dostarczanie solidnych ustawień domyślnych i specjalistycznych narzędzi, które pomagają deweloperom ulepszać aspekty wygody użytkowników, takie jak wydajność, ułatwienia dostępu, bezpieczeństwo i gotowość do działania na urządzeniach mobilnych. W 2021 roku skupiliśmy się na pomaganiu stosom platform w poprawie wrażeń użytkowników i Core Web Vitals.

Jednym z najważniejszych kroków na drodze do osiągnięcia naszego celu, jakim jest poprawa wydajności platformy, było zbadanie idealnej sekwencji wczytywania skryptów innych firm w Next.js. Platformy takie jak Next.js są wyjątkowo dobrze przygotowane do udostępniania przydatnych ustawień domyślnych i funkcji, które pomagają deweloperom efektywnie wczytywać zasoby, w tym zasoby innych firm. Przeanalizowaliśmy obszerne archiwum HTTP i dane Lighthouse, aby dowiedzieć się, które usługi innych firm najczęściej blokują renderowanie w różnych platformach.

Aby rozwiązać problem ze skryptami innych firm, które blokują główny wątek, utworzyliśmy komponent Script. Komponent zawiera funkcje sekwencjonowania, które zapewniają deweloperom lepszą kontrolę nad wczytywaniem skryptów innych firm.

Sekwencjonowanie skryptów innych firm bez komponentu platformy

Dostępne wskazówki dotyczące zmniejszania wpływu skryptów blokujących renderowanie zawierają te metody skutecznego wczytywania i sekwencjonowania skryptów innych firm:

  1. Użyj atrybutu async lub defer z tagami <script>, aby poinformować przeglądarkę, że ma wczytywać niekrytyczne skrypty spoza witryny bez blokowania parsera dokumentów. Skrypty, które nie są wymagane do początkowego wczytania strony lub pierwszej interakcji użytkownika, mogą być uznane za niekrytyczne.

       <script src="https://example.com/script1.js" defer></script>
       <script src="https://example.com/script2.js" async></script>
    
  2. Nawiązywanie wczesnych połączeń z wymaganymi źródłami za pomocą wskazówek preconnect i dns-prefetch. Dzięki temu krytyczne skrypty mogą zacząć się pobierać wcześniej.

       <head>
           <link rel="preconnect" href="http://PreconnThis.com">
           <link rel="dns-prefetch" href="http://PrefetchThis.com">
       </head>
    
  3. Leniwe ładowanie zasobów i elementów osadzonych pochodzących od innych firm po zakończeniu wczytywania głównej zawartości strony lub gdy użytkownik przewinie stronę w dół do miejsca, w którym się znajdują.

Komponent skryptu Next.js

Komponent skryptu Next.js implementuje powyższe metody sekwencjonowania skryptów i udostępnia deweloperom szablon do definiowania strategii wczytywania. Po określeniu odpowiedniej strategii czcionka będzie ładować się optymalnie, nie blokując innych kluczowych zasobów.

Komponent Script bazuje na tagu HTML <script> i umożliwia ustawienie priorytetu wczytywania skryptów innych firm za pomocą atrybutu strategy.

// Example for beforeInteractive:
<Script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver" strategy="beforeInteractive" />

// Example for afterInteractive (default):
<Script src="https://example.com/samplescript.js" />

// Example for lazyonload:
<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />

Atrybut strategy może mieć 3 wartości.

  1. beforeInteractive: tej opcji można używać w przypadku skryptów o kluczowym znaczeniu, które powinny zostać wykonane, zanim strona stanie się interaktywna. Next.js dba o to, aby takie skrypty były wstrzykiwane do początkowego kodu HTML na serwerze i wykonywane przed innym samodzielnie spakowanym kodem JavaScript. Dobrymi kandydatami do tej strategii są skrypty do zarządzania zgodą użytkowników, skrypty do wykrywania botów lub biblioteki pomocnicze wymagane do renderowania kluczowych treści.

  2. afterInteractive: jest to domyślna strategia, która odpowiada wczytywaniu skryptu z atrybutem defer. Należy go używać w przypadku skryptów, które przeglądarka może uruchomić po tym, jak strona stanie się interaktywna, np. skryptów analitycznych. Next.js wstrzykuje te skrypty po stronie klienta i są one uruchamiane po nawodnieniu strony. Dlatego, o ile nie określono inaczej, wszystkie skrypty innych firm zdefiniowane za pomocą komponentu Script są odraczane przez Next.js, co zapewnia silne ustawienie domyślne.

  3. lazyOnload: tej opcji można użyć do leniwego ładowania skryptów o niskim priorytecie, gdy przeglądarka jest bezczynna. Funkcje udostępniane przez takie skrypty nie są wymagane natychmiast po tym, jak strona stanie się interaktywna – na przykład wtyczki do czatu lub mediów społecznościowych.

Deweloperzy mogą poinformować Next.js, jak ich aplikacja używa skryptu, określając strategię. Dzięki temu platforma może stosować optymalizacje i sprawdzone metody, aby wczytywać skrypt, zapewniając jednocześnie najlepszą sekwencję wczytywania.

Za pomocą komponentu Script deweloperzy mogą umieścić skrypt innej firmy w dowolnym miejscu w aplikacji w przypadku skryptów innych firm, które mają być wczytywane później, oraz na poziomie dokumentu w przypadku skryptów krytycznych. Oznacza to, że komponent Script może być umieszczony w tym samym miejscu co komponent korzystający ze skryptu. Po hydratacji skrypt zostanie wstrzyknięty do nagłówka początkowo wyrenderowanego dokumentu lub na dole treści, w zależności od zastosowanej strategii.

Pomiar wpływu

Użyliśmy szablonów aplikacji handlowej i bloga Next.js, aby utworzyć 2 aplikacje demonstracyjne, które pomogły zmierzyć wpływ skryptów innych firm. Najczęściej wybierani dostawcy usług innych firm w przypadku Menedżera tagów Google i umieszczania treści z mediów społecznościowych byli początkowo umieszczani bezpośrednio na stronach tych aplikacji, a następnie za pomocą komponentu Script. Następnie porównaliśmy skuteczność tych stron w narzędziu WebPageTest.

Skrypty innych firm w aplikacji handlowej Next.js

Do szablonu aplikacji handlowej na potrzeby wersji demonstracyjnej dodano skrypty innych firm, jak pokazano poniżej.

Przed Po
Menedżer tagów Google z tagiem asynchronicznym Komponent skryptu ze strategią = afterInteractive dla obu skryptów
Przycisk Obserwuj na Twitterze bez atrybutu async lub defer
Konfiguracja skryptu i komponentu skryptu dla wersji demonstracyjnej 1 z 2 skryptami.

Poniższe porównanie pokazuje postęp wizualny w przypadku obu wersji zestawu startowego Next.js Commerce. Jak widać, LCP występuje prawie o 1 sekundę wcześniej, gdy komponent Script jest włączony z odpowiednią strategią wczytywania.

Porównanie pasków miniatur pokazujące poprawę LCP

Skrypty innych firm na blogu Next.js

Skrypty innych firm zostały dodane do aplikacji bloga demonstracyjnego w sposób opisany poniżej.

Przed Po
Menedżer tagów Google z tagiem asynchronicznym Komponent skryptu ze strategią = lazyonload dla każdego z 4 skryptów
Przycisk Obserwuj na Twitterze z asynchronicznym ładowaniem
Przycisk subskrypcji YouTube bez atrybutu async lub defer
Przycisk LinkedIn Follow bez atrybutu async lub defer
Konfiguracja skryptu i komponentu skryptu w przypadku wersji demonstracyjnej 2 z 4 skryptami.
Film pokazujący postęp wczytywania strony indeksu z komponentem Script i bez niego. Dzięki komponentowi Script czas FCP skrócił się o 0,5 sekundy.

Jak widać na filmie, pierwsze wyrenderowanie treści (FCP) następuje po 0,9 sekundy na stronie bez komponentu Script i po 0,4 sekundy z komponentem Script.

Co dalej z komponentem Skrypt

Opcje strategii dla afterInteractive i lazyOnload zapewniają znaczną kontrolę nad skryptami blokującymi renderowanie, ale badamy też inne opcje, które zwiększyłyby użyteczność komponentu Script.

Korzystanie z procesów internetowych

Skrypty Web Worker mogą służyć do uruchamiania niezależnych skryptów w wątkach działających w tle, co może zwolnić wątek główny do obsługi zadań interfejsu i poprawić wydajność. Skrypty Web Worker najlepiej nadają się do odciążania wątku głównego z przetwarzania kodu JavaScript, a nie z obsługi interfejsu. Skrypty używane do obsługi klienta lub marketingu, które zwykle nie wchodzą w interakcję z interfejsem, mogą być dobrymi kandydatami do wykonywania w wątku w tle. Do odizolowania takich skryptów w procesie roboczym można użyć niewielkiej biblioteki innej firmy – PartyTown.

W przypadku bieżącej implementacji komponentu skryptu Next.js zalecamy odroczenie tych skryptów w głównym wątku przez ustawienie strategii na afterInteractive lub lazyOnload. W przyszłości planujemy wprowadzić nową opcję strategii, 'worker', która pozwoli Next.js używać PartyTown lub niestandardowego rozwiązania do uruchamiania skryptów w procesach roboczych. Zachęcamy deweloperów do przesyłania komentarzy na temat tego dokumentu RFC.

Minimalizowanie CLS

Elementy umieszczone przez osoby trzecie, takie jak reklamy, filmy czy kanały w mediach społecznościowych, mogą powodować przesunięcia układu, gdy są ładowane leniwie. Wpływa to na wrażenia użytkowników i dane dotyczące skumulowanego przesunięcia układu (CLS) na stronie. Wskaźnik CLS można zminimalizować, określając rozmiar kontenera, w którym zostanie wczytany osadzony element.

Komponent Script może służyć do wczytywania elementów umieszczonych na stronie, które mogą powodować przesunięcia układu. Rozważamy rozszerzenie go o opcje konfiguracji, które pomogą zmniejszyć CLS. Może to być dostępne w samym komponencie Skrypt lub jako komponent towarzyszący.

Komponenty owijające

Składnia i strategia wczytywania popularnych skryptów innych firm, takich jak Google Analytics czy Menedżer tagów Google (GTM), są zwykle stałe. Można je dodatkowo umieścić w osobnych komponentach opakowujących dla każdego typu skryptu. Deweloperzy będą mieli dostęp tylko do minimalnego zestawu atrybutów aplikacji (takich jak identyfikator śledzenia). Komponenty opakowujące pomogą deweloperom w następujący sposób:

  1. Ułatwianie im dodawania popularnych tagów skryptów.
  2. Zapewnienie, że platforma używa najbardziej optymalnej strategii.

Podsumowanie

Skrypty innych firm są zwykle tworzone w celu dodania określonych funkcji do witryny, która z nich korzysta. Aby zmniejszyć wpływ skryptów niekrytycznych, zalecamy ich odroczenie, co domyślnie robi komponent skryptu Next.js. Deweloperzy mają pewność, że dołączone skrypty nie opóźnią działania kluczowych funkcji, chyba że wyraźnie zastosują strategię beforeInteractive. Podobnie jak w przypadku komponentu skryptu Next.js, deweloperzy platform mogą rozważyć utworzenie tych funkcji na innych platformach. Wspólnie z zespołem Nuxt.js aktywnie pracujemy nad wprowadzeniem podobnego komponentu. Na podstawie opinii chcemy też ulepszyć komponent skryptu, aby obsługiwał dodatkowe przypadki użycia.

Podziękowania

Dziękujemy Karze Erickson, Janicklasowi Ralphowi, Katie Hempenius, Philipowi Waltonowi, Jeremy'emu Wagnerowi i Addy'emu Osmani za opinie na temat tego posta.