Optymalizacja zewnętrznych skryptów przez Web Workery, Partytown i Next.js
- Czas potrzebny na przeczytanie:4 minuty
- Opublikowane:
Performance w 2022 roku to jedna z najbardziej kluczowych rzeczy podczas budowania stron i aplikacji webowych. Poza najważniejszymi odczuciami użytkownika, wiemy, że Google bierze pod uwagę metryki wydajności podczas pozycjonowania. Czasem nie ważne jak bardzo byśmy się starali, pomimo optymalizacji obrazków i fontów, wynik Lighthouse dla naszego projektu może świecić się cały na czerwono... Zewnętrzne skrypty mogą być powodem!
Problem
Ładujesz Google Analytics, Tag Manager, skrypty Facebooka, dymka wiadomości... Uff, sporo tego, prawda? A teraz wyobraź sobie, że to wszystko ląduje w głównym wątku razem z całym potrzebnym JavaScriptem do prawidłowego funkcjonowania Twojej aplikacji. Nie brzmi to najlepiej. Przy dobrym połączeniu sieciowym ten problem może nie mieć aż takiego znaczenia, ale jeśli spojrzymy na to, jak działają strony na urządzeniach mobilnych, to zaczynamy dostrzegać pewne komplikacje.
Optymalizacja skryptów w Next.js
Pierwszym krokiem by usprawnić proces pobierania skryptów, byłoby dodanie atrybutków async
/ defer
do tagu <script>
. Dzięki temu nie blokujemy parsowania dokumentu HTML. Przydałoby się również dodanie preconnect i dns prefetch, dzięki nim szybciej zaadresujemy chęć pobrania czegoś z zewnętrznej domeny. To brzmi jak... kawałek niepotrzebnego boilerplate'u. Na szczęście twórcy Next.js zadbali o Developer Experience i wprowadzili ich autorski komponent <Script>
:
<Script src="..." strategy="beforeInteractive" />
<Script src="..." strategy="afterInteractive" />
<Script src="..." strategy="lazyOnload" />
beforeInteractive
- przeciwieństwo tego, z czym chcemy dziś walczyć. Krytyczne skrypty, które chcemy odpalać jeszcze przed tym, jak strona będzie interaktywnaafterInteractive
- ładuje skrypty tuż po osiągnięciu pełnej interaktywności strony. Sprawdzi się np. dla Google Analytics / Tag ManagerlazyOnload
- ostatnie skrypty w kolejce. Jak już się wszystko załadowało, to lecimy właśnie z nimi. Idealne dla rzeczy, które mogą być ładowane "w tle", np. dymek wiadomości
To wszystko brzmi naprawdę świetnie i faktycznie Next robi za nas zajebistą robotę w optymalizacji skryptów. Wady? Nadal obciążamy główny wątek.
Web Workery i Partytown
Rozwiązanie naszego problemu? Web Workery. Działają one z dala od głównego wątku, nie ingerując w skrypty potrzebne do interaktywności strony...Hola hola, Web Workery? Przecież system komunikacji pomiędzy Web Workerem, a głównym wątkiem jest asynchroniczny, przez co Web Worker nie ma bezpośredniego dostępu do API przeglądarki. Jest to dużym problemem dla zewnętrznych skryptów, bowiem działają one często w oparciu o DOM.
Partytown to leciutka biblioteka, która rozwiązuje ten problem.
Pozwala ona na synchroniczną komunikację Web Workera z głównym wątkiem, jest swego rodzaju proxy. Poza dostępem do DOM, mamy możliwość zarządzania dostępem do niektórych API, np. możemy zablokować dostęp do document.cookie
, czy uniemożliwić zapisywanie skryptowi do localStorage
.
Partytown i Next.js w praktyce
Partytown to biblioteka typu framework agnostic, działa ze zwykłym HTML, React, Nuxt, Remix oraz od niedawna z dedykowanym komponentem <Script>
w Next. Zacznijmy od instalacji:
npm install @builder.io/partytown
Next.js "natywnie" wspiera Partytown dopiero w wersji eksperymentalnej, żeby z niej skorzystać musimy uzupełnić konfigurację:
next.config.js
module.exports = {
experimental: {
nextScriptWorkers: true,
},
}
Zostało nam tylko odpalenie projektu i wykorzystanie strategy="worker"
w komponencie <Script>
, od teraz nasz główny wątek będzie wolny od skryptów third-party.
<Script src="..." strategy="worker" />
Debugowanie
Chociaż Partytown nie wymaga żadnej dodatkowej konfiguracji, to przydatna może okazać opcja debug
. Aby ją odblokować, musimy dodać skrypt z atrybutem data-partytown-config
do Next Document:
pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html>
<Head>
<script
data-partytown-config
dangerouslySetInnerHTML={{
__html: `
partytown = {
lib: "/_next/static/~partytown/",
debug: true
};
`,
}}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
Jeśli coś pójdzie nie po naszej myśli, to dzięki debug: true
zobaczymy to zapewne w logach:
Wady Partytown
To wszystko wydaje się być zbyt piękne, prawda? Jak wszystko w programowaniu, Partytown posiada wiele wad i ograniczeń. Dla mnie największym blockerem jest to, że biblioteka jest bardzo świeżym i niedojrzałym rozwiązaniem. Jakie trade-offy się kryją po stronie technicznej?
- biblioteka nie jest przyjazna dla skryptów operujących na DOM i wprowadzających interaktywność z UI. Wszystkie operacje na DOM w Workerze są celowo opóźniane, przez co będą wolniejsze od tradycyjnego operowania na głównym wątku.
- konieczność wprowadzanie proxy żądania dla zewnętrznych skryptów, które nie korzystają z nagłówka
Access-Control-Allow-Origin: *
. - brak wsparcia dla event.preventDefault().
- ograniczenia w obsłudze ciasteczek i localStorage dla cross-origin iframe
Partytown vs Next Script
Pytanie, które narodziło się w mojej głowie, kiedy poznawałem Partytown, to:
"Kiedy tak właściwie korzystać ze zwykłych skryptów, a kiedy je delegować do Web Workera?".
Pomijając już niedojrzałość tego rozwiązania, Partytown sprawdzi się świetnie dla skryptów, które mogą działać nieco "w tle" - Google Analytics, czy Facebook Pixel to dobre przykłady. Wszystkie inne skrypty, które wymagają interakcji z DOM, warto wziąć pod lupę. Jeśli tych operacji jest trochę, to raczej bez zastanowienia skłaniałbym się ku normalnym skryptom w Next - będzie po prostu szybciej 🚀
Podsumowanie
Partytown pomimo wszystkich swoich wad, jest naprawdę bardzo ciekawym narzędziem do optymalizacji zewnętrznych skryptów - w końcu dajemy odetchnąć naszemu głównemu wątkowi!
Bardzo się cieszę, że twórcy bibliotek coraz większy nacisk kładą właśnie na performance, to realnie przekłada się na odczucia użytkownika :)
A Ty co sądzisz o Partytown?