Next.js ve Gatsby'deki daha yeni bir webpack parçalama stratejisi, sayfa yükleme performansını artırmak için yinelenen kodu en aza indirir.
Chrome, JavaScript açık kaynak ekosistemindeki araçlar ve çerçevelerle işbirliği yapıyor. Next.js ve Gatsby'nin yükleme performansını artırmak için kısa süre önce çeşitli yeni optimizasyonlar eklendi. Bu makalede, artık her iki çerçevede de varsayılan olarak gönderilen iyileştirilmiş ayrıntılı parçalama stratejisi ele alınmaktadır.
Giriş
Birçok web çerçevesi gibi Next.js ve Gatsby de temel paketleyici olarak webpack'i kullanır. webpack v3, farklı giriş noktaları arasında paylaşılan modüllerin tek bir (veya birkaç) "commons" parçası (veya parçaları) halinde çıkışını mümkün kılmak için CommonsChunkPlugin
özelliğini kullanıma sundu. Paylaşılan kod ayrı olarak indirilebilir ve tarayıcı önbelleğinde erken aşamada depolanabilir. Bu da daha iyi yükleme performansı sağlayabilir.
Bu kalıp, birçok tek sayfalık uygulama çerçevesi tarafından benimsenerek giriş noktası ve paket yapılandırması oluşturmak için popüler hale geldi.
Pratik bir yaklaşım olsa da tüm paylaşılan modül kodunu tek bir parçada birleştirme kavramının sınırlamaları vardır. Her giriş noktasında paylaşılmayan modüller, kullanılmadığı rotalar için indirilebilir. Bu durum, gerekenden daha fazla kodun indirilmesine neden olur. Örneğin, page1
, common
parçasını yüklediğinde page1
, moduleC
kullanmamasına rağmen moduleC
kodunu yükler.
Bu ve birkaç nedenden dolayı webpack v4, eklentiyi kaldırıp yeni bir eklenti olan SplitChunksPlugin
'yi kullanmaya başladı.
Geliştirilmiş Parçalama
SplitChunksPlugin
için varsayılan ayarlar çoğu kullanıcı için uygundur. Birden fazla rotada yinelenen kodun getirilmesini önlemek için bir dizi koşula bağlı olarak birden fazla bölünmüş parça oluşturulur.
Ancak bu eklentiyi kullanan birçok web çerçevesi, parça bölme konusunda hâlâ "tek ortak" yaklaşımını izlemektedir. Örneğin Next.js, sayfaların% 50'sinden fazlasında kullanılan tüm modülleri ve tüm çerçeve bağımlılıklarını (react
, react-dom
vb.) içeren bir commons
paketi oluşturur.
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)[\\/]/,
},
},
},
Çerçeveye bağımlı kodu paylaşılan bir parçaya dahil etmek, bu kodun herhangi bir giriş noktası için indirilip önbelleğe alınabileceği anlamına gelse de sayfaların yarısından fazlasında kullanılan ortak modülleri dahil etmeye yönelik kullanıma dayalı sezgisel yöntem çok etkili değildir. Bu oranı değiştirmenin yalnızca iki sonucu olabilir:
- Oranı düşürürseniz daha fazla gereksiz kod indirilir.
- Oranı artırırsanız birden fazla rota genelinde daha fazla kod yinelenir.
Next.js, bu sorunu çözmek için farklı bir yapılandırma benimseyerekSplitChunksPlugin
herhangi bir rota için gereksiz kodu azaltır.
- Yeterince büyük olan tüm üçüncü taraf modülleri (160 KB'tan büyük) kendi bağımsız parçalarına bölünür.
- Çerçeve bağımlılıkları (
react
,react-dom
vb.) için ayrı birframeworks
parçası oluşturulur. - Gerektiği kadar paylaşılan parça oluşturulur (en fazla 25).
- Oluşturulacak bir parçanın minimum boyutu 20 KB olarak değiştirildi.
Bu ayrıntılı parçalama stratejisi aşağıdaki avantajları sunar:
- Sayfa yüklenme süreleri iyileştirilir. Tek bir ortak parça yerine birden fazla ortak parça yayınlamak, herhangi bir giriş noktası için gereksiz (veya yinelenen) kod miktarını en aza indirir.
- Gezinme sırasında önbelleğe alma işlemi iyileştirildi. Büyük kitaplıkları ve çerçeve bağımlılıklarını ayrı parçalara bölmek, her ikisi de yükseltme yapılana kadar değişmeyeceğinden önbelleğin geçersiz kılınma olasılığını azaltır.
Next.js'in benimsediği yapılandırmanın tamamını webpack-config.ts
adresinde görebilirsiniz.
Daha fazla HTTP isteği
SplitChunksPlugin
, ayrıntılı parçalama için temeli tanımladı ve bu yaklaşımı Next.js gibi bir çerçeveye uygulamak tamamen yeni bir kavram değildi. Ancak birçok çerçeve, birkaç nedenden dolayı tek bir sezgisel yöntem ve "ortak" paket stratejisi kullanmaya devam etti. Bu durum, çok daha fazla HTTP isteğinin site performansını olumsuz etkileyebileceği endişesini de içerir.
Tarayıcılar tek bir kaynağa yalnızca sınırlı sayıda TCP bağlantısı açabilir (Chrome için 6). Bu nedenle, bir paketleyici tarafından oluşturulan parça sayısını en aza indirmek, toplam istek sayısının bu eşiğin altında kalmasını sağlayabilir. Ancak bu durum yalnızca HTTP/1.1 için geçerlidir. HTTP/2'deki çoğullama, tek bir bağlantı üzerinden tek bir kaynak kullanılarak birden fazla isteğin paralel olarak yayınlanmasına olanak tanır. Diğer bir deyişle, genellikle paketleyicimiz tarafından yayınlanan parçaların sayısını sınırlama konusunda endişelenmemize gerek yoktur.
Tüm büyük tarayıcılar HTTP/2'yi destekler. Chrome ve Next.js ekipleri, Next.js'in tek "commons" paketini birden fazla paylaşılan parçaya bölerek istek sayısını artırmanın yükleme performansını herhangi bir şekilde etkileyip etkilemeyeceğini görmek istedi. maxInitialRequests
özelliğini kullanarak paralel isteklerin maksimum sayısını değiştirirken tek bir sitenin performansını ölçerek başladılar.
Tek bir web sayfasında birden fazla denemenin ortalama üç çalıştırılmasında, maksimum ilk istek sayısı (5'ten 15'e) değiştirildiğinde load
, start-render ve First Contentful Paint süreleri yaklaşık olarak aynı kaldı. İlginç bir şekilde, performansta küçük bir ek yükün yalnızca yüzlerce isteğe agresif bir şekilde bölündükten sonra ortaya çıktığını fark ettik.
Bu, güvenilir bir eşiğin (20-25 istek) altında kalmanın yükleme performansı ve önbelleğe alma verimliliği arasında doğru dengeyi sağladığını gösterdi. Temel testlerden sonra maxInitialRequest
sayısı 25 olarak belirlendi.
Paralel olarak gerçekleşen maksimum istek sayısını değiştirme işlemi, birden fazla paylaşılan paketle sonuçlandı. Bunları her giriş noktası için uygun şekilde ayırmak, aynı sayfada gereksiz kod miktarını önemli ölçüde azalttı.
Bu deneme yalnızca sayfa yükleme performansında olumsuz bir etki olup olmayacağını görmek için istek sayısını değiştirmekle ilgiliydi. Sonuçlar, test sayfasında maxInitialRequests
değerini 25
olarak ayarlamanın en iyi seçenek olduğunu gösteriyor. Bu ayar, sayfayı yavaşlatmadan JavaScript yük boyutunu küçültüyor. Sayfayı doldurmak için gereken toplam JavaScript miktarı yaklaşık olarak aynı kaldı. Bu nedenle, kod miktarı azaldığı halde sayfa yükleme performansı iyileşmedi.
webpack, oluşturulacak bir parça için varsayılan minimum boyut olarak 30 KB'ı kullanır. Ancak 25'lik bir maxInitialRequests
değeri ile 20 KB'lık minimum boyutun birleştirilmesi daha iyi bir önbelleğe alma işlemiyle sonuçlandı.
Ayrıntılı parçalarla boyut küçültme
Next.js de dahil olmak üzere birçok çerçeve, her rota geçişi için daha yeni komut dosyası etiketleri eklemek üzere istemci tarafı yönlendirmeye (JavaScript tarafından işlenir) dayanır. Ancak bu dinamik parçaları oluşturma zamanında nasıl önceden belirlerler?
Next.js, hangi çıkış parçalarının farklı giriş noktaları tarafından kullanıldığını belirlemek için sunucu tarafında oluşturulan bir bildirim dosyası kullanır. Bu bilgileri istemciye de sağlamak için her giriş noktasının tüm bağımlılıklarını eşlemek üzere kısaltılmış bir istemci tarafı derleme manifest dosyası oluşturuldu.
// 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}`)) || []
)
}

Bu yeni ayrıntılı parçalama stratejisi ilk olarak Next.js'te bir işaretin arkasında kullanıma sunuldu ve burada bir dizi ilk kullanıcı üzerinde test edildi. Birçok kullanıcı, sitelerinin tamamında kullanılan toplam JavaScript miktarında önemli ölçüde azalma olduğunu gördü:
Web sitesi | Toplam JS Değişikliği | % Farkı |
---|---|---|
https://www.barnebys.com/ | -238 KB | -%23 |
https://sumup.com/ | -220 KB | -%30 |
https://www.hashicorp.com/ | -11 MB | -%71 |
Son sürüm, varsayılan olarak 9.2 sürümünde gönderildi.
Gatsby
Gatsby, ortak modülleri tanımlamak için kullanıma dayalı bir sezgisel yöntem kullanma konusunda aynı yaklaşımı izlerdi:
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)[\\/]/,
},
Webpack yapılandırmalarını benzer bir ayrıntılı parçalama stratejisini benimseyecek şekilde optimize ederek birçok büyük sitede JavaScript'te önemli ölçüde azalma olduğunu da fark ettiler:
Web sitesi | Toplam JS Değişikliği | % Farkı |
---|---|---|
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 |
Bu mantığı, v2.20.7'de varsayılan olarak gönderilen webpack yapılandırmalarına nasıl uyguladıklarını anlamak için PR'ye göz atın.
Sonuç
Küçük parçalar halinde gönderme kavramı Next.js, Gatsby veya webpack'e özgü değildir. Kullanılan çerçeve veya modül paketleyiciden bağımsız olarak, büyük bir "ortak" paket yaklaşımını izleyen uygulamaların parçalama stratejilerini iyileştirmeleri gerekir.
- Aynı parçalama optimizasyonlarının vanilla React uygulamasına uygulanmasını görmek istiyorsanız örnek React uygulamasına göz atın. Bu uygulama, ayrıntılı parçalama stratejisinin basitleştirilmiş bir sürümünü kullanır ve aynı türde mantığı sitenize uygulamaya başlamanıza yardımcı olabilir.
- Rollup için parçalar varsayılan olarak ayrıntılı bir şekilde oluşturulur. Davranışı manuel olarak yapılandırmak istiyorsanız
manualChunks
bölümüne göz atın.