Kod bölmeli JavaScript

Büyük JavaScript kaynaklarının yüklenmesi sayfa hızını önemli ölçüde etkiler. JavaScript'inizi daha küçük parçalara bölmek ve yalnızca başlangıç sırasında bir sayfanın çalışması için gerekenlerin indirilmesi, sayfanızın yükleme duyarlılığını büyük ölçüde iyileştirebilir. Bu da sayfanızın Sonraki Boyamayla Etkileşimi (INP) iyileştirebilir.

Bir sayfa büyük JavaScript dosyalarını indirdikten, ayrıştırıp derlediğinde bir süre yanıt vermeyebilir. Sayfa öğeleri, sayfanın ilk HTML'sinin bir parçası oldukları ve stillerini CSS tarafından belirlendiği için görünür durumdadır. Ancak bu etkileşimli öğeleri desteklemek için gereken JavaScript ve sayfa tarafından yüklenen diğer komut dosyaları, bu öğelerin çalışması için JavaScript'i ayrıştırıyor ve yürütüyor olabilir. Sonuç olarak kullanıcı, etkileşimin önemli ölçüde geciktiği veya tamamen kopmuş gibi hissedebilir.

Bu durum genellikle JavaScript'in ana iş parçacığında ayrıştırılıp derlendiği için ana iş parçacığının engellenmesinden kaynaklanır. Bu işlem çok uzun sürerse etkileşimli sayfa öğeleri kullanıcı girişine yeterince hızlı yanıt vermeyebilir. Bunun bir çözümü, kod bölme olarak bilinen bir teknikle diğer JavaScript'lerin daha sonra yüklenmesini ertelemek ve yalnızca sayfanın çalışması için gereken JavaScript'i yüklemektir. Bu modül, bu iki teknikten ikincisine odaklanmaktadır.

Kod bölme aracılığıyla başlatma sırasında JavaScript ayrıştırma ve yürütme işlemlerini azaltın

Lighthouse, JavaScript yürütmesi 2 saniyeden uzun sürdüğünde ve 3,5 saniyeden uzun sürdüğünde başarısız olduğunda bir uyarı verir. Aşırı düzeyde JavaScript ayrıştırma ve yürütme, sayfa yaşam döngüsünün herhangi bir noktasında potansiyel bir sorundur. Çünkü kullanıcının sayfayla etkileşimde bulunduğu zaman, JavaScript'in işlenmesinden ve çalıştırılmasından sorumlu ana iş parçacığı görevlerinin çalıştığı ana denk geliyorsa bir etkileşimin giriş gecikmesini artırma potansiyeli vardır.

Üstelik, JavaScript'in aşırı şekilde çalıştırılması ve ayrıştırılması özellikle sayfa yaşam döngüsünde kullanıcıların sayfayla etkileşimde bulunma olasılığının çok yüksek olduğu nokta olduğu için ilk sayfa yükleme sırasında sorun oluşturur. Bir yük yanıt metriği olan Toplam Engelleme Süresi (TBT), INP ile yüksek ilişkilidir. Bu da kullanıcıların ilk sayfa yükleme sırasında etkileşim deneme eğiliminin yüksek olduğunu gösterir.

Sayfanızın istediği her bir JavaScript dosyasını yürütmek için harcanan süreyi bildiren Lighthouse denetimi, tam olarak hangi komut dosyalarının kod bölme için aday olabileceğini belirlemenize yardımcı olması açısından yararlıdır. Ardından, sayfa yükleme sırasında sayfanın JavaScript'inin tam olarak hangi bölümlerinin kullanılmadığını belirlemek için Chrome Geliştirici Araçları'ndaki kapsam aracını kullanarak daha ayrıntılı ilerleyebilirsiniz.

Kod bölme, bir sayfanın ilk JavaScript yüklerini azaltabilen yararlı bir tekniktir. Bir JavaScript paketini iki bölüme ayırmanıza olanak tanır:

  • JavaScript'in sayfa yükleme sırasında kullanılması gerekir ve bu nedenle, başka bir zamanda yüklenemez.
  • Daha sonra, genellikle kullanıcının sayfadaki belirli bir etkileşimli öğeyle etkileşim kurduğu noktada yüklenebilen kalan JavaScript.

Kod bölme işlemi, dinamik import() söz dizimi kullanılarak yapılabilir. Bu söz dizimi, başlatma sırasında belirli bir JavaScript kaynağını isteyen <script> öğelerinin aksine, sayfa yaşam döngüsünün sonraki bir noktasında JavaScript kaynağı için istekte bulunur.

document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
  // Get the form validation named export from the module through destructuring:
  const { validateForm } = await import('/validate-form.mjs');

  // Validate the form:
  validateForm();
}, { once: true });

Önceki JavaScript snippet'inde validate-form.mjs modülü yalnızca bir kullanıcı bir formun <input> alanlarını bulanıklaştırdığında indirilir, ayrıştırılır ve yürütülür. Bu durumda, formun doğrulama mantığını yönlendirmeden sorumlu JavaScript kaynağı, sayfayla yalnızca, gerçekten kullanılma olasılığı en yüksek olduğunda devreye girer.

webpack, Parcel, Rollup ve esbuild gibi JavaScript paketleyicileri, kaynak kodunuzda dinamik import() çağrısıyla karşılaştıklarında JavaScript paketlerini daha küçük parçalara bölecek şekilde yapılandırılabilir. Bu araçların çoğu bunu otomatik olarak yapar, ancak özellikle esbuild, bu optimizasyona dahil olmanızı gerektirir.

Kod bölmeyle ilgili yararlı notlar

Kod bölme, ilk sayfa yükleme sırasında ana iş parçacığı anlaşmazlığını azaltmak için etkili bir yöntem olsa da, kod bölme fırsatları için JavaScript kaynak kodunuzu denetlemeye karar verirseniz birkaç noktayı aklınızda tutmanız faydalı olacaktır.

Mümkünse bir paketleyici kullanın

Geliştiricilerin, geliştirme sürecinde JavaScript modüllerini kullanmaları yaygın bir uygulamadır. Kod okunabilirliğini ve sürdürülebilirliğini artıran mükemmel bir geliştirici deneyimi iyileştirmesidir. Ancak JavaScript modüllerini üretime gönderirken ortaya çıkabilecek bazı optimum performans özellikleri yoktur.

En önemlisi, kod bölmeyi planladığınız modüller de dahil olmak üzere kaynak kodunuzu işlemek ve optimize etmek için bir paketleyici kullanmanız gerekir. Paketleyiciler, yalnızca JavaScript kaynak koduna optimizasyonlar uygulama konusunda değil, paket boyutu ile sıkıştırma oranı gibi performans konuları arasında denge kurmada da oldukça etkilidir. Sıkıştırmanın etkisi paket boyutuyla birlikte artar, ancak paketleyiciler de paketlerin komut dosyası değerlendirmesi nedeniyle uzun görevler gerektirecek kadar büyük olmadığından emin olmaya çalışır.

Paketleyiciler, ağ üzerinden çok sayıda paketlenmemiş modülün gönderim sorunundan da kaçınır. JavaScript modülleri kullanan mimarilerde, büyük, karmaşık modül ağaçları yaygındır. Modül ağaçları ayrıldığında her modül ayrı bir HTTP isteğini temsil eder ve modülleri paketlemezseniz web uygulamanızdaki etkileşim gecikebilir. Büyük modül ağaçlarını mümkün olduğunca erken yüklemek için <link rel="modulepreload"> kaynak ipucunu kullanmak mümkün olsa da, yükleme performansı açısından JavaScript paketleri tercih edilmeye devam eder.

Akış derlemesini yanlışlıkla devre dışı bırakmayın

Chromium'un V8 JavaScript motoru, üretim JavaScript kodunuzun mümkün olduğunca verimli bir şekilde yüklenmesini sağlamak için birçok optimizasyon seçeneği sunar. Bu optimizasyonlardan biri akış derlemesi olarak bilinir. Bu derleme, (tarayıcıya aktarılan HTML'nin artımlı olarak ayrıştırılması) ağdan gelen JavaScript parçalarını derler.

Chromium'daki web uygulamanız için akış derlemesinin gerçekleşmesini sağlamanın birkaç yolu vardır:

  • JavaScript modüllerini kullanmaktan kaçınmak için üretim kodunuzu dönüştürün. Paketleyiciler, bir derleme hedefine göre JavaScript kaynak kodunuzu dönüştürebilir ve hedef genellikle belirli bir ortama özgü olur. V8, modül kullanmayan herhangi bir JavaScript koduna akış derlemesi uygular. Siz de paketleyicinizi, JavaScript modül kodunuzu, JavaScript modüllerini ve özelliklerini kullanmayan bir söz dizimine dönüştürecek şekilde yapılandırabilirsiniz.
  • JavaScript modüllerini üretime göndermek istiyorsanız .mjs uzantısını kullanın. Üretim JavaScript'inizin modül kullanıp kullanmamasından bağımsız olarak modül kullanan JavaScript için, modül kullanmayan JavaScript'e karşılık özel bir içerik türü yoktur. V8 söz konusu olduğunda .js uzantısını kullanarak JavaScript modüllerini üretim kanalına gönderdiğinizde, akış derlemesini etkin bir şekilde devre dışı bırakırsınız. JavaScript modülleri için .mjs uzantısını kullanırsanız V8, modül tabanlı JavaScript kodu için akış derlemesinin bozulmamasını sağlayabilir.

Bu düşüncelerin, sizi kod bölme özelliğinden caydırmasına izin vermeyin. Kod bölme, kullanıcıların ilk JavaScript yüklerini azaltmanın etkili bir yoludur. Ancak bir paketleyici kullanarak ve V8'in akış derleme davranışını nasıl koruyabileceğinizi öğrendiğinizde, üretim JavaScript kodunuzun kullanıcılar için mümkün olduğunca hızlı olmasını sağlayabilirsiniz.

Dinamik içe aktarma demosu

web paketi

webpack, SplitChunksPlugin adlı bir eklentiyle birlikte gelir. Bu eklenti, paketleyicinin JavaScript dosyalarını nasıl böleceğini yapılandırmanıza olanak tanır. webpack hem dinamik import() hem de statik import ifadelerini tanır. SplitChunksPlugin davranışı, yapılandırmasında chunks seçeneği belirtilerek değiştirilebilir:

  • chunks: async, varsayılan değerdir ve dinamik import() çağrılarını ifade eder.
  • chunks: initial, statik import çağrılarını ifade eder.
  • chunks: all, hem dinamik import() hem de statik içe aktarma işlemlerini kapsar. Böylece, parçaları async ile initial içe aktarma işlemleri arasında paylaşabilirsiniz.

Varsayılan olarak, webpack bir dinamik import() ifadesiyle karşılaştığında bu modül için ayrı bir yığın oluşturur:

/* main.js */

// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';

myFunction('Hello world!');

// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
  // Assumes top-level await is available. More info:
  // https://v8.dev/features/top-level-await
  await import('/form-validation.js');
}

Önceki kod snippet'inin varsayılan web paketi yapılandırması, iki ayrı parçayla sonuçlanır:

  • Web paketinin initial yığını olarak sınıflandırdığı ve main.js ve ./my-function.js modülünü içeren main.js parçası.
  • Yalnızca form-validation.js içeren async parçası (yapılandırılmışsa kaynak adında bir dosya karması içerir). Bu parça, yalnızca condition truthy olduğunda indirilir.

Bu yapılandırma, form-validation.js parçasının yüklenmesini, gerçekten gerekli olana kadar ertelemenizi sağlar. Bu, ilk sayfa yükleme sırasında komut dosyası değerlendirme süresini azaltarak yükleme duyarlılığını iyileştirebilir. form-validation.js parçası için komut dosyası indirme ve değerlendirme, belirtilen bir koşul karşılandığında dinamik olarak içe aktarılan modül indirilir. Çoklu dolgunun yalnızca belirli bir tarayıcı için indirildiği veya önceki örnekte olduğu gibi içe aktarılan modülün kullanıcı etkileşimi için gerekli olduğu bir koşul bu duruma örnek olarak verilebilir.

Diğer yandan, SplitChunksPlugin yapılandırmasını chunks: initial değerini belirtecek şekilde değiştirmek, kodun yalnızca ilk parçalarda bölünmesini sağlar. Bunlar, statik olarak içe aktarılan veya web paketinin entry özelliğinde listelenen gibi parçalardır. Yukarıdaki örneğe baktığımızda, elde edilen yığın, tek bir komut dosyası içinde form-validation.js ve main.js öğelerinin bir kombinasyonu olur. Bu da ilk sayfa yükleme performansının potansiyel olarak daha kötü olmasına yol açar.

SplitChunksPlugin seçenekleri, daha büyük komut dosyalarını birden çok küçük komut dosyasına ayıracak şekilde de yapılandırılabilir. Örneğin, maxSize seçeneği kullanılarak web paketine, maxSize tarafından belirtilen sınırı aşan parçaları ayrı dosyalara bölme talimatı verebilirsiniz. Büyük komut dosyası dosyalarını daha küçük dosyalara bölmek, yükleme duyarlılığını artırabilir. Bazı durumlarda CPU yoğun komut dosyası değerlendirme çalışması, daha küçük görevlere bölünür. Bu görevler, ana iş parçacığını daha uzun süre engelleme olasılığından daha düşüktür.

Ayrıca, daha büyük JavaScript dosyaları oluşturmak, komut dosyalarının önbelleği geçersiz kılma riskiyle karşı karşıya olduğu anlamına da gelir. Örneğin, hem çerçeve hem de birinci taraf uygulama koduyla çok büyük bir komut dosyası gönderirseniz yalnızca çerçeve güncellenirse paketin tamamı geçersiz kılınabilir, ancak gruplandırılmış kaynakta başka hiçbir şey güncellenemez.

Diğer yandan, daha küçük komut dosyası dosyaları, geri gelen ziyaretçinin kaynakları önbellekten alma olasılığını artırarak yinelenen ziyaretlerde sayfaların daha hızlı yüklenmesini sağlar. Bununla birlikte, küçük dosyalar sıkıştırmadan büyük dosyalara göre daha az fayda sağlar ve ekimsiz bir tarayıcı önbelleğiyle sayfa yüklemelerinde ağ gidiş dönüş süresini artırabilir. Önbelleğe alma verimliliği, sıkıştırma verimliliği ve komut dosyası değerlendirme süresi arasında denge kurmaya dikkat edilmelidir.

web paketi demosu

webpack SplitChunksPlugin demosu hakkında daha fazla bilgi edinin.

Bilginizi test etme

Kod bölme işlemi sırasında hangi import ifadesi türü kullanılır?

Dinamik import().
Doğru.
Statik import.
Tekrar deneyin.

Hangi import ifadesi türü, JavaScript modülünün üst kısmında, başka bir konumda olmamalıdır?

Dinamik import().
Tekrar deneyin.
Statik import.
Doğru.

Web paketinde SplitChunksPlugin kullanırken async yığını ile initial yığını arasındaki fark nedir?

async parçaları dinamik import() kullanılarak yüklenir ve initial parçaları statik import kullanılarak yüklenir.
Doğru.
async parçaları statik import kullanılarak yüklenir ve initial parçalar dinamik import() kullanılarak yüklenir.
Tekrar deneyin.

Sıradaki: Resimleri ve <iframe> öğelerini geç yükle

Genellikle oldukça pahalı bir kaynak türü olsa da, yüklenmesini erteleyebileceğiniz tek kaynak türü JavaScript değildir. Görüntü ve <iframe> öğeleri kendi başlarına da yüksek maliyetli kaynaklardır. JavaScript'e benzer şekilde, resimlerin ve <iframe> öğesinin yüklenmesini erteleyebilirsiniz. Bu işlem, bu kursun bir sonraki modülünde geç yüklenerek açıklanmıştır.