Komut dosyası değerlendirmesi ve uzun görevler

Komut dosyaları yüklenirken tarayıcının bunları yürütmeden önce değerlendirmesi zaman alır ve bu durum uzun görevlere neden olabilir. Komut dosyası değerlendirmesinin nasıl çalıştığını ve sayfa yükleme sırasında uzun görevlere neden olmasını önlemek için neler yapabileceğinizi öğrenin.

Interaction to Next Paint (INP) metriğini optimize etme konusunda karşılaşacağınız tavsiyelerin çoğu, etkileşimlerin kendisini optimize etmeye yöneliktir. Örneğin, uzun görevleri optimize etme kılavuzunda setTimeout ile sonuç verme gibi teknikler ele alınır. Bu teknikler, uzun süren görevlerden kaçınarak ana iş parçacığına biraz zaman tanır. Bu sayede, etkileşimler ve diğer etkinlikler tek bir uzun süren görevin tamamlanmasını beklemek yerine daha erken çalıştırılabilir.

Ancak, komut dosyalarını yüklemekten kaynaklanan uzun görevler ne olacak? Bu görevler, kullanıcı etkileşimlerini engelleyebilir ve yükleme sırasında sayfanın INP'sini etkileyebilir. Bu rehberde, tarayıcıların komut dosyası değerlendirmesiyle başlatılan görevleri nasıl işlediği ve sayfa yüklenirken ana iş parçacığınızın kullanıcı girişine daha duyarlı olması için komut dosyası değerlendirme işini nasıl bölebileceğiniz ele alınacaktır.

Komut dosyası değerlendirmesi nedir?

Çok fazla JavaScript içeren bir uygulamanın profilini oluşturduysanız Evaluate Script olarak etiketlenen uzun görevler görmüş olabilirsiniz.

Chrome Geliştirici Araçları'nın performans profil oluşturucusunda görselleştirildiği şekilde komut dosyası değerlendirme çalışması. Bu işlem, başlatma sırasında uzun bir göreve neden olur ve ana ileti dizisinin kullanıcı etkileşimlerine yanıt verme özelliğini engeller.
Chrome Geliştirici Araçları'ndaki performans profil oluşturucuda gösterildiği gibi komut dosyası değerlendirme çalışması. Bu durumda, iş, ana iş parçacığının kullanıcı etkileşimlerini yönlendiren görevler de dahil olmak üzere diğer işleri yapmasını engelleyen uzun bir göreve neden olacak kadar büyüktür.

JavaScript, yürütülmeden hemen önce derlendiğinden komut dosyası değerlendirmesi, tarayıcıda JavaScript'in yürütülmesi için gereklidir. Bir komut dosyası değerlendirilirken önce hatalar için ayrıştırılır. Ayrıştırıcı hata bulmazsa komut dosyası bayt koduna derlenir ve yürütülmeye devam edebilir.

Gerekli olsa da kullanıcılar, ilk oluşturulmasından kısa bir süre sonra bir sayfayla etkileşim kurmaya çalışabileceğinden komut dosyası değerlendirmesi sorunlu olabilir. Ancak bir sayfanın oluşturulmuş olması, yüklenmesinin tamamlandığı anlamına gelmez. Sayfa, komut dosyalarını değerlendirmekle meşgul olduğundan yükleme sırasında gerçekleşen etkileşimler gecikebilir. Bu noktada bir etkileşimin gerçekleşebileceği garanti edilmese de (bununla ilgili komut dosyası henüz yüklenmemiş olabilir) JavaScript'e bağlı ve hazır etkileşimler olabilir veya etkileşim tamamen JavaScript'e bağlı olmayabilir.

Komut dosyaları ile bunları değerlendiren görevler arasındaki ilişki

Komut dosyası değerlendirmesinden sorumlu görevlerin nasıl başlatılacağı, yüklediğiniz komut dosyasının normal bir <script> öğesiyle mi yoksa type=module ile yüklenen bir modül mü olduğuna bağlıdır. Tarayıcılar, işlemleri farklı şekilde ele alma eğiliminde olduğundan, büyük tarayıcı motorlarının komut dosyası değerlendirmesini nasıl ele aldığı, komut dosyası değerlendirme davranışlarının farklılık gösterdiği yerlerde ele alınacaktır.

<script> öğesiyle yüklenen komut dosyaları

Komut dosyalarını değerlendirmek için gönderilen görevlerin sayısı genellikle bir sayfadaki <script> öğelerinin sayısıyla doğrudan ilişkilidir. Her <script> öğesi, istenen komut dosyasının ayrıştırılması, derlenmesi ve yürütülmesi için değerlendirileceği bir görevi başlatır. Bu durum Chromium tabanlı tarayıcılar, Safari ve Firefox için geçerlidir.

Bu neden önemli? Üretim komut dosyalarınızı yönetmek için bir paketleyici kullandığınızı ve sayfanızın çalışması için gereken her şeyi tek bir komut dosyasında paketleyecek şekilde yapılandırdığınızı varsayalım. Web sitenizde bu durum geçerliyse söz konusu komut dosyasını değerlendirmek için tek bir görev gönderileceğini düşünebilirsiniz. Bu olumsuz bir durum mu? Bu, büyük bir komut dosyası olmadığı sürece gerekli değildir.

Büyük JavaScript parçalarını yüklemekten kaçınarak komut dosyası değerlendirme işini bölebilir ve ek <script> öğelerini kullanarak daha fazla sayıda ayrı ve küçük komut dosyası yükleyebilirsiniz.

Sayfa yükleme sırasında her zaman mümkün olduğunca az JavaScript yüklemeye çalışmanız gerekse de komut dosyalarınızı bölmek, ana iş parçacığını engelleyebilecek tek bir büyük görev yerine, ana iş parçacığını hiç engellemeyen veya en azından başlangıçta olduğundan daha az engelleyen daha fazla sayıda küçük görev olmasını sağlar.

Chrome Geliştirici Araçları&#39;nın performans profil oluşturucusunda görselleştirildiği gibi, komut dosyası değerlendirmesi içeren birden fazla görev. Daha az sayıda büyük komut dosyası yerine daha çok sayıda küçük komut dosyası yüklendiğinden görevlerin uzun görevlere dönüşme olasılığı azalır. Bu sayede ana iş parçacığı, kullanıcı girişine daha hızlı yanıt verebilir.
Sayfanın HTML'sinde birden fazla <script> öğesi bulunduğundan komut dosyalarını değerlendirmek için birden fazla görev oluşturuldu. Bu, ana iş parçacığını engelleme olasılığı daha yüksek olan tek bir büyük komut dosyası paketi göndermek yerine tercih edilir.

Senaryo değerlendirmesi için görevleri bölmeyi, etkileşim sırasında çalışan etkinlik geri çağırmaları sırasında kontrolü bırakmaya benzetebilirsiniz. Ancak komut dosyası değerlendirmesinde, kontrolü başka bir işleme bırakma mekanizması, yüklediğiniz JavaScript'i ana iş parçacığını engelleme olasılığı daha yüksek olan daha az sayıda büyük komut dosyası yerine birden fazla küçük komut dosyasına böler.

<script> öğesi ve type=module özelliğiyle yüklenen komut dosyaları

Artık <script> öğesinde type=module özelliği ile ES modüllerini tarayıcıya yerel olarak yüklemek mümkün. Bu komut dosyası yükleme yaklaşımı, özellikle import maps ile birlikte kullanıldığında, kodu üretim kullanımı için dönüştürmek zorunda kalmamak gibi bazı geliştirici deneyimi avantajları sunar. Ancak komut dosyalarını bu şekilde yüklemek, tarayıcıdan tarayıcıya farklılık gösteren görevleri planlar.

Chromium tabanlı tarayıcılar

Chrome gibi tarayıcılarda veya Chrome'dan türetilen tarayıcılarda, type=module özelliği kullanılarak ES modüllerinin yüklenmesi, normalde type=module özelliği kullanılmadığında gördüğünüzden farklı türde görevler üretir. Örneğin, her modül komut dosyası için Modülü derle olarak etiketlenen etkinliği içeren bir görev çalıştırılır.

Chrome Geliştirici Araçları&#39;nda görselleştirildiği gibi, modül derleme çalışması birden fazla görevde gerçekleştirilir.
Chromium tabanlı tarayıcılarda modül yükleme davranışı. Her modül komut dosyası, değerlendirmeden önce içeriklerini derlemek için bir Compile module (Modülü derle) çağrısı oluşturur.

Modüller derlendikten sonra, bunlarda çalıştırılan tüm kodlar Modülü değerlendir olarak etiketlenen etkinliği başlatır.

Bir modülün Chrome Geliştirici Araçları&#39;nın performans panelinde görselleştirildiği gibi tam zamanında değerlendirilmesi.
Bir modüldeki kod çalıştırıldığında bu modül tam zamanında değerlendirilir.

Buradaki etki (en azından Chrome ve ilgili tarayıcılarda), ES modülleri kullanılırken derleme adımlarının bölünmesidir. Bu, uzun görevleri yönetme açısından açık bir kazançtır. Ancak ortaya çıkan modül değerlendirme çalışması, kaçınılmaz bir maliyete katlanmanız anlamına gelir. Mümkün olduğunca az JavaScript göndermeye çalışmanız gerekse de tarayıcıdan bağımsız olarak ES modüllerini kullanmak aşağıdaki avantajları sağlar:

  • Tüm modül kodu, yüksek düzey modunda otomatik olarak çalıştırılır. Bu mod, JavaScript motorlarının katı olmayan bir bağlamda yapılamayacak olası optimizasyonları yapmasına olanak tanır.
  • type=module kullanılarak yüklenen komut dosyaları, varsayılan olarak ertelenmiş gibi değerlendirilir. Bu davranışı değiştirmek için async özelliği, type=module ile yüklenen komut dosyalarında kullanılabilir.

Safari ve Firefox

Modüller Safari ve Firefox'ta yüklendiğinde her biri ayrı bir görevde değerlendirilir. Bu, teorik olarak yalnızca statik import ifadelerden oluşan tek bir üst düzey modülü diğer modüllere yükleyebileceğiniz ve yüklenen her modülün değerlendirilmesi için ayrı bir ağ isteği ve görev oluşturulacağı anlamına gelir.

Dinamik import() ile yüklenen komut dosyaları

Dinamik import(), komut dosyalarını yüklemenin başka bir yöntemidir. Bir ES modülünün en üstünde bulunması gereken statik import ifadelerinin aksine, dinamik import() çağrısı, JavaScript parçası yüklemek için komut dosyasının herhangi bir yerinde görünebilir. Bu tekniğe kod bölme denir.

Dinamik import(), INP'yi iyileştirme konusunda iki avantaj sunar:

  1. Daha sonra yüklenmek üzere ertelenen modüller, o sırada yüklenen JavaScript miktarını azaltarak başlangıç sırasında ana iş parçacığı çekişmesini azaltır. Bu sayede ana iş parçacığı serbest kalır ve kullanıcı etkileşimlerine daha hızlı yanıt verebilir.
  2. Dinamik import() çağrıları yapıldığında her çağrı, her modülün derlenmesini ve değerlendirilmesini kendi görevine ayırır. Elbette, çok büyük bir modülü yükleyen dinamik bir import(), oldukça büyük bir komut dosyası değerlendirme görevini başlatır. Etkileşim, dinamik import() çağrısıyla aynı anda gerçekleşirse bu durum, ana iş parçacığının kullanıcı girişine yanıt verme yeteneğini etkileyebilir. Bu nedenle, mümkün olduğunca az JavaScript yüklemeniz hâlâ çok önemlidir.

Dinamik import() çağrıları, tüm büyük tarayıcı motorlarında benzer şekilde çalışır: Sonuç olarak ortaya çıkan komut dosyası değerlendirme görevleri, dinamik olarak içe aktarılan modül sayısı kadar olur.

Web çalışanı içinde yüklenen komut dosyaları

Web çalışanları, özel bir JavaScript kullanım alanıdır. Web çalışanları ana iş parçacığına kaydedilir ve çalışandaki kod kendi iş parçacığında çalışır. Bu, web çalışanı kaydeden kod ana iş parçacığında çalışırken web çalışanı içindeki kodun çalışmaması açısından büyük avantaj sağlar. Bu sayede ana iş parçacığındaki tıkanıklık azalır ve ana iş parçacığının kullanıcı etkileşimlerine daha duyarlı olması sağlanabilir.

Web çalışanları, ana iş parçacığı işini azaltmanın yanı sıra kendileri de çalışan bağlamında kullanılacak harici komut dosyalarını yükleyebilir. Bu işlem, modül çalışanlarını destekleyen tarayıcılarda importScripts veya statik import ifadeleri aracılığıyla yapılabilir. Sonuç olarak, bir web çalışanı tarafından istenen tüm komut dosyaları ana iş parçacığı dışında değerlendirilir.

Artıları ve eksileri değerlendirme

Komut dosyalarınızı ayrı ve daha küçük dosyalara bölmek, daha az sayıda ancak çok daha büyük dosyalar yüklemeye kıyasla uzun görevleri sınırlamaya yardımcı olsa da komut dosyalarını nasıl böleceğinize karar verirken bazı şeyleri göz önünde bulundurmanız önemlidir.

Sıkıştırma verimliliği

Senaryoları bölme konusunda sıkıştırma önemli bir faktördür. Komut dosyaları küçüldüğünde sıkıştırma biraz daha az verimli hale gelir. Daha büyük komut dosyaları, sıkıştırmadan çok daha fazla yararlanır. Sıkıştırma verimliliğini artırmak, komut dosyalarının yükleme sürelerini mümkün olduğunca düşük tutmaya yardımcı olsa da komut dosyalarını, başlatma sırasında daha iyi etkileşim sağlamak için yeterince küçük parçalara ayırdığınızdan emin olmak biraz denge gerektirir.

Paketleyiciler, web sitenizin bağlı olduğu komut dosyalarının çıkış boyutunu yönetmek için ideal araçlardır:

  • Webpack söz konusu olduğunda SplitChunksPlugin eklentisi yardımcı olabilir. Öğe boyutlarını yönetmeye yardımcı olacak seçenekler için SplitChunksPlugin dokümanlarına bakın.
  • Rollup ve esbuild gibi diğer paketleyiciler için kodunuzda dinamik import() çağrıları kullanarak komut dosyası boyutu yönetebilirsiniz. Bu paketleyiciler ve webpack, dinamik olarak içe aktarılan öğeyi otomatik olarak kendi dosyasına ayırarak daha büyük başlangıç paketi boyutlarını önler.

Önbelleği geçersiz kılma

Önbellek geçersiz kılma, bir sayfanın tekrar ziyaretlerde ne kadar hızlı yüklendiğinde büyük rol oynar. Büyük ve tek bir dosyadan oluşan komut dosyası paketleri gönderdiğinizde tarayıcı önbelleğe alma konusunda dezavantajlı olursunuz. Bunun nedeni, birinci taraf kodunuzu güncellediğinizde (paketleri güncelleyerek veya hata düzeltmelerini göndererek) paketin tamamının geçersiz hale gelmesi ve tekrar indirilmesi gerekmesidir.

Komut dosyalarınızı bölerek yalnızca komut dosyası değerlendirme işini daha küçük görevlere bölmekle kalmaz, aynı zamanda geri gelen ziyaretçilerin ağ yerine tarayıcı önbelleğinden daha fazla komut dosyası alması olasılığını da artırırsınız. Bu da genel olarak daha hızlı sayfa yükleme anlamına gelir.

İç içe yerleştirilmiş modüller ve yükleme performansı

Üretimde ES modülleri gönderiyorsanız ve bunları type=module özelliğiyle yüklüyorsanız modül yerleştirmenin başlangıç süresini nasıl etkileyebileceğinin farkında olmanız gerekir. Modül iç içe yerleştirme, bir ES modülünün statik olarak başka bir ES modülünü içe aktarması ve bu modülün de statik olarak başka bir ES modülünü içe aktarması anlamına gelir:

// a.js
import {b} from './b.js';

// b.js
import {c} from './c.js';

ES modülleriniz birlikte paketlenmemişse yukarıdaki kod bir ağ isteği zinciriyle sonuçlanır: a.js, <script> öğesinden istendiğinde b.js için başka bir ağ isteği gönderilir. Bu istek de c.js için başka bir isteği içerir. Bunu önlemenin bir yolu, paketleyici kullanmaktır. Ancak paketleyicinizi, komut dosyası değerlendirme işini yaymak için komut dosyalarını ayıracak şekilde yapılandırdığınızdan emin olun.

Paketleyici kullanmak istemiyorsanız iç içe yerleştirilmiş modül çağrılarını aşmanın bir başka yolu da modulepreload kaynak ipucunu kullanmaktır. Bu ipucu, ağ isteği zincirlerini önlemek için ES modüllerini önceden yükler.

Sonuç

Tarayıcıdaki komut dosyalarının değerlendirmesini optimize etmek zorlu bir iştir. Yaklaşım, web sitenizin gereksinimlerine ve kısıtlamalarına bağlıdır. Ancak komut dosyalarını bölerek komut dosyası değerlendirme işini çok sayıda küçük görev arasında yayarsınız. Bu nedenle, ana ileti dizisini engellemek yerine ana ileti dizisine kullanıcı etkileşimlerini daha verimli bir şekilde işleme olanağı tanırsınız.

Özetlemek gerekirse büyük senaryo değerlendirme görevlerini bölmek için yapabileceklerinizden bazıları şunlardır:

  • type=module özelliği olmadan <script> öğesini kullanarak komut dosyalarını yüklerken çok büyük olan komut dosyalarını yüklemekten kaçının. Bu komut dosyaları, ana iş parçacığını engelleyen kaynak yoğun komut dosyası değerlendirme görevlerini başlatır. Bu çalışmayı bölmek için senaryolarınızı daha fazla <script> öğesine yayın.
  • ES modüllerini tarayıcıda yerel olarak yüklemek için type=module özelliğini kullanmak, her ayrı modül komut dosyası için değerlendirme amacıyla ayrı görevler başlatır.
  • Dinamik import() çağrıları kullanarak ilk paketlerinizin boyutunu küçültün. Bu, paketleyicilerde de çalışır. Paketleyiciler, dinamik olarak içe aktarılan her modülü "bölünme noktası" olarak değerlendirir. Bu da dinamik olarak içe aktarılan her modül için ayrı bir komut dosyası oluşturulmasına neden olur.
  • Sıkıştırma verimliliği ve önbelleği geçersiz kılma gibi ödünleri değerlendirdiğinizden emin olun. Daha büyük komut dosyaları daha iyi sıkıştırılır ancak daha az görevde daha maliyetli komut dosyası değerlendirme çalışması içerme ve tarayıcı önbelleğinin geçersiz kılınmasına neden olma olasılığı daha yüksektir. Bu da genel olarak daha düşük önbelleğe alma verimliliğine yol açar.
  • ES modüllerini paketlemeden yerel olarak kullanıyorsanız başlatma sırasında yüklenmelerini optimize etmek için modulepreload kaynak ipucunu kullanın.
  • Her zaman olduğu gibi, mümkün olduğunca az JavaScript gönderin.

Bu kesinlikle bir denge işidir. Ancak komut dosyalarını bölerek ve dinamik import() ile ilk yükleri azaltarak daha iyi başlangıç performansı elde edebilir ve bu kritik başlangıç döneminde kullanıcı etkileşimlerini daha iyi karşılayabilirsiniz. Bu, INP metriğinde daha iyi puan almanıza ve dolayısıyla daha iyi bir kullanıcı deneyimi sunmanıza yardımcı olur.