Nowsza strategia podziału pakietów internetowych na fragmenty w Next.js i Gatsby ogranicza liczbę powielania kodu i zwiększa wydajność wczytywania strony.
Chrome współpracuje z narzędziami w ekosystemie open source JavaScriptu. W ostatnim czasie wprowadziliśmy kilka nowszych optymalizacji dodano, by poprawić wydajność wczytywania Next.js oraz Gatsby W tym artykule omawiamy udoskonaloną strategię dzielenia na fragmenty który jest teraz domyślnie dostarczany w obu tych usługach.
Wprowadzenie
Podobnie jak wiele innych platform internetowych, Next.js i Gatsby korzystają z pakietu webpack
. Wprowadzenie pakietu webpack v3
CommonsChunkPlugin
, aby umożliwić
moduły wyjściowe współdzielone między różnymi punktami wejścia w jednym (lub kilku) „wspólnych”, fragment (lub
fragmenty). Współdzielony kod można pobrać oddzielnie i zapisać w pamięci podręcznej przeglądarki, dzięki czemu będzie można
poprawia wydajność wczytywania.
Ten wzorzec stał się popularny, gdy na wielu platformach aplikacji jednostronicowych zastosowano punkt początkowy konfiguracja pakietu wyglądała tak:
Chociaż jest to praktyczne, koncepcja łączenia całego udostępnionego kodu modułu w jeden fragment ma swoje
i ograniczeniach. Moduły nieudostępniane w każdym punkcie wejścia można pobrać dla tras, które z niego nie korzystają
co powoduje pobieranie za dużo kodu. Na przykład: gdy wczytuje się page1
we fragmencie common
, wczytuje kod dla moduleC
, mimo że page1
nie używa moduleC
.
Z tego powodu (wraz z kilkoma innymi) pakiet internetowy w wersji 4 usunął wtyczkę na rzecz
jeden: SplitChunksPlugin
.
Ulepszone dzielenie na fragmenty
Domyślne ustawienia funkcji SplitChunksPlugin
są odpowiednie dla większości użytkowników. Wiele podzielonych fragmentów jest
tworzone w zależności od kilku warunków
aby zapobiec pobieraniu zduplikowanego kodu na wielu trasach.
Jednak wiele platform internetowych, które korzystają z tej wtyczki, nadal korzysta z kodu „single-commons”. podejście do fragmentu
dzielone na dwie części. Next.js wygenerowałby na przykład pakiet commons
zawierający dowolny moduł,
używane w ponad 50% stron i we wszystkich zależnościach od platformy (react
, react-dom
itd.).
const splitChunksConfigs = {
…
prod: {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
commons: {
name: 'commons',
chunks: 'all',
minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
},
react: {
name: 'commons',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|scheduler|use-subscription)[\\/]/,
},
},
},
Mimo że umieszczenie we wspólnym fragmencie kodu zależnego od platformy oznacza, że można go pobrać w pamięci podręcznej dla dowolnego punktu wejścia, opartą na wykorzystaniu heurystykę uwzględniania typowych modułów używanych w więcej niż połowa stron nie jest zbyt skuteczna. Zmiana tego współczynnika może przynieść tylko jeden z dwóch rezultatów:
- Jeśli zmniejszysz współczynnik proporcji, zostanie pobrany więcej niepotrzebnego kodu.
- Jeśli zwiększysz ten współczynnik, więcej kodu zostanie powielone na wielu trasach.
Aby rozwiązać ten problem, zespół Next.js wdrożył inny
w wersji SplitChunksPlugin
, która zmniejsza
zbędny kod dla żadnej trasy.
- Każdy wystarczająco duży moduł firmy zewnętrznej (większy niż 160 KB) jest dzielony na osobne moduły fragment
- Tworzony jest oddzielny fragment
frameworks
dla zależności platformy (react
,react-dom
i i tak dalej). - Utworzono tyle udostępnianych fragmentów, ile jest potrzebnych (maksymalnie 25)
- Minimalny rozmiar generowanego fragmentu zmienia się na 20 KB
Ta strategia szczegółowego dzielenia na fragmenty przynosi takie korzyści:
- Czas wczytywania stron wydłużył się. Jeśli zamiast jednego wysyłam kilka wspólnych fragmentów, minimalizuje ilość zbędnego (lub zduplikowanego) kodu dla każdego punktu wejścia.
- Poprawione buforowanie podczas nawigacji. Dzielenie dużych bibliotek i zależności od platform na oddzielne fragmenty zmniejsza prawdopodobieństwo unieważnienia pamięci podręcznej, ponieważ jest mało prawdopodobne, zmian do momentu przeprowadzenia uaktualnienia.
Możesz zobaczyć całą konfigurację wdrożoną przez Next.js w webpack-config.ts
.
Więcej żądań HTTP
Organizacja SplitChunksPlugin
określiła podstawę szczegółowego dzielenia na fragmenty i stosuje to podejście do
platforma taka jak Next.js nie była zupełnie nową koncepcją. Wiele platform nadal jednak
za pomocą jednej heurystyki i typu „commons” strategii dotyczących pakietów z kilku powodów. Obejmuje to obawy, które
znacznie więcej żądań HTTP może negatywnie wpłynąć na wydajność witryny.
Przeglądarki mogą otworzyć ograniczoną liczbę połączeń TCP z jednym punktem początkowym (6 w przypadku Chrome), więc zminimalizowanie liczby fragmentów generowanych przez pakiet pakujący może sprawić, że całkowita liczba żądań nie przekracza tego progu. Dotyczy to jednak tylko protokołu HTTP/1.1. Multipleks w HTTP/2 umożliwia równoległe przesyłanie wielu żądań przez jedno połączenie pochodzeniu danych. Innymi słowy, nie musimy się martwić o ograniczenie liczby fragmentów. udostępnianych przez naszego partnera ds. pakietów.
Wszystkie popularne przeglądarki obsługują HTTP/2. Zespoły Chrome i Next.js
Postanowiłem sprawdzić, czy zwiększy to liczbę żądań, dzieląc pojedyncze zasoby „commons” firmy Next.js. pakiet
na wiele współdzielonych fragmentów w żaden sposób wpłynie na wydajność wczytywania. Zaczęła od pomiaru
wydajności pojedynczej witryny przy jednoczesnym modyfikowaniu maksymalnej liczby równoległych żądań za pomocą funkcji
maxInitialRequests
usłudze.
Średnio trzy uruchomienia wielu prób na jednej stronie internetowej
load
start-render
i Pierwsze wyrenderowanie treści pozostały mniej więcej takie same przy różnicowaniu maksymalnej wartości początkowej
liczby żądań (od 5 do 15). Co ciekawe, zaobserwowaliśmy tylko niewielki wzrost skuteczności
po agresywnym podzieleniu ich na setki żądań.
Pokazało to, że utrzymanie odpowiedniego progu (20–25 żądań) zapewniło odpowiednią równowagę
między wydajnością wczytywania a wydajnością buforowania. Po przeprowadzeniu testów podstawowych wybrano 25 jako
maxInitialRequest
.
Zmodyfikowanie maksymalnej liczby żądań realizowanych równolegle spowodowało więcej niż jeden udostępniany pakiet, a oddzielenie go od punktu wejścia znacznie ograniczyło zbędny kod na stronie.
Eksperyment polegał wyłącznie na modyfikacji liczby żądań w celu sprawdzenia, czy będą one
mają negatywny wpływ na wydajność wczytywania strony. Wyniki sugerują, że ustawienie maxInitialRequests
na
Komponent 25
na stronie testowej był optymalny, ponieważ zmniejszył rozmiar ładunku JavaScript bez spowolnienia
w dół strony. Pozostała ilość kodu JavaScript potrzebna do nawodnienia strony
co wyjaśnia, dlaczego wydajność wczytywania strony niekoniecznie poprawiała się
z dużą ilością kodu.
Webpack używa domyślnego rozmiaru minimalnego do wygenerowania fragmentu 30 KB. Połączenie
Parametr maxInitialRequests
ma wartość 25 przy minimalnym rozmiarze 20 KB, ponieważ zapewnia lepsze buforowanie.
Zmniejszanie rozmiaru przy użyciu szczegółowych fragmentów
Wiele platform, w tym Next.js, korzysta z routingu po stronie klienta (obsługiwanego przez JavaScript) w celu wstrzykiwania nowszych tagów skryptu w przypadku każdego przejścia na trasie. Jak jednak wstępnie określają te dynamiczne fragmenty na etapie kompilacji?
Next.js korzysta z pliku manifestu kompilacji po stronie serwera, aby określić, które wysyłane fragmenty są używane przez różne punkty wejścia. Aby przekazać te informacje klientowi, skrócona wersja po jego stronie utworzono plik manifestu kompilacji w celu zmapowania wszystkich zależności dla każdego punktu wejścia.
// Returns a promise for the dependencies for a particular route
getDependencies (route) {
return this.promisedBuildManifest.then(
man => (man[route] && man[route].map(url => `/_next/${url}`)) || []
)
}
Ta nowsza strategia szczegółowego dzielenia na fragmenty została po raz pierwszy wdrożona w Next.js za flagą, gdzie została przetestowana użytkowników wczesnej wersji. Wiele firm zaobserwowało znaczny spadek całkowitej liczby skryptów JavaScript używanych w swoich cała witryna:
Witryna | Całkowita zmiana JS | % różnicy |
---|---|---|
https://www.barnebys.com/ | –238 KB | –23% |
https://sumup.com/ | –220 KB | –30% |
https://www.hashicorp.com/ | –11 MB | –71% |
Ostateczna wersja była domyślnie wysyłana w wersji 9.2.
Gatsby
W przypadku interfejsu Gatsby stosowane było to samo podejście: użycie heurystyka do definiowania wspólnych modułów:
config.optimization = {
…
splitChunks: {
name: false,
chunks: `all`,
cacheGroups: {
default: false,
vendors: false,
commons: {
name: `commons`,
chunks: `all`,
// if a chunk is used more than half the components count,
// we can assume it's pretty global
minChunks: componentsCount > 2 ? componentsCount * 0.5 : 2,
},
react: {
name: `commons`,
chunks: `all`,
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
},
Optymalizacja konfiguracji pakietu internetowego pod kątem podobnej szczegółowej strategii dzielenia na fragmenty można zauważyć znaczny spadek JavaScriptu w wielu dużych witrynach:
Witryna | Całkowita zmiana JS | % różnicy |
---|---|---|
https://www.gatsbyjs.org/ | –680 KB | –22% |
https://www.thirdandgrove.com/ | –390 KB | -25% |
https://ghost.org/ | -1,1 MB | –35% |
https://reactjs.org/ | -80 KB | -8% |
Przeczytaj informacje PR, aby dowiedzieć się, jak wdrożyli tę logikę w konfiguracji pakietu internetowego, który jest wysyłany domyślnie w wersji 2.20.7.
Podsumowanie
Koncepcja wyświetlania szczegółowych informacji o dostawie nie dotyczy Next.js, Gatsby czy nawet pakietu internetowego. Wszyscy powinni rozważyć ulepszenie strategii podziału aplikacji na fragmenty, jeśli jest ona zgodna z rozsądnymi zasadami niezależnie od użytej platformy lub narzędzia do tworzenia pakietów modułów.
- Jeśli chcesz zobaczyć te same optymalizacje w zakresie dzielenia na fragmenty zastosowane w aplikacji React obejrzyj tę przykładową reakcję Wykorzystuje uproszczona wersja strategii szczegółowego dzielenia na fragmenty i pomoże Ci zacząć stosować tę samą do Twojej witryny.
- W przypadku podsumowania fragmenty są domyślnie tworzone szczegółowo z zachowaniem szczegółowości. Zwróć uwagę na:
manualChunks
, jeśli chcesz ręcznie nie musisz niczego konfigurować.