Kritik Oluşturma Yolu Performansını Analiz Etme

Ilya Grigorik
Ilya Grigorik

Oluşturma yolu performansıyla ilgili kritik performans sorunlarını tespit etmek ve çözmek için sık karşılaşılan güçlükler hakkında iyi bilgi sahibi olmanız gerekir. Birlikte uygulamalı bir tura katılarak, sayfalarınızı optimize etmenize yardımcı olacak yaygın performans modellerini çıkaralım.

Kritik oluşturma yolunun optimize edilmesi, tarayıcının sayfayı olabildiğince hızlı boyamasını sağlar: Sayfaların hızlanması; etkileşimin, görüntülenen sayfa sayısının ve dönüşümün artmasını sağlar. Ziyaretçinin boş bir ekranı görüntülerken geçirdiği süreyi en aza indirmek için hangi kaynakların hangi sırayla yükleneceğini optimize etmemiz gerekir.

Bu süreci açıklamak için mümkün olan en basit durumla başlayalım ve sayfamızı ek kaynaklar, stiller ve uygulama mantığı içerecek şekilde adım adım geliştirelim. Bu süreçte her durumu optimize edeceğiz ve nerede yanlış gidebileceğini de göreceğiz.

Şimdiye kadar, kaynak (CSS, JS veya HTML dosyası) işlenmek üzere hazır olduktan sonra tarayıcıda neler olduğuna odaklandık. Kaynağı önbellekten veya ağdan getirmek için gereken süreyi göz ardı ettik. Aşağıdakilerin geçerli olduğunu varsayıyoruz:

  • Sunucuya yapılan bir ağ gidiş dönüşünün (yayılma gecikmesi) 100 ms'ye mal olur.
  • Sunucu yanıt süresi, HTML dokümanı için 100 ms ve diğer tüm dosyalar için 10 ms.

Hello world deneyimi

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Critical Path: No Style</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Deneyin

CSS veya JavaScript olmaksızın temel HTML işaretlemesi ve tek bir resimle başlayacağız. Chrome Geliştirici Araçları'nda Ağ zaman çizelgemizi açıp kaynak şelalesini inceleyelim:

CRP

Beklendiği gibi, HTML dosyasının indirilmesi yaklaşık 200 ms sürdü. Mavi çizginin şeffaf kısmının, tarayıcının herhangi bir yanıt baytı almadan ağda beklediği süreyi temsil ettiğini, sabit bölümün ise ilk yanıt baytları alındıktan sonra indirme işlemini tamamlama süresini gösterdiğini unutmayın. İndirilen HTML çok küçüktür (4K'dan az). Bu nedenle, tüm dosyayı getirmek için tek bir döngü yeterli olur. Sonuç olarak, HTML belgesinin getirilmesi yaklaşık 200 ms sürer. Zamanın yarısı ağda beklerken diğer yarısı sunucu yanıtını bekler.

HTML içeriği kullanıma sunulduğunda tarayıcı baytları ayrıştırır, jetonlara dönüştürür ve DOM ağacını oluşturur. Geliştirici Araçları'nın, DOMContentLoaded etkinliği için altta bulunan süreyi (216 ms) rahatlıkla bildirdiğine dikkat edin. Bu süre, aynı zamanda mavi dikey çizgiye de karşılık gelir. İndirilen HTML dosyasının sonu ile mavi dikey çizgi (DOMContentLoaded), tarayıcının DOM ağacını oluşturması için gereken süredir (bu örnekte yalnızca birkaç milisaniye).

"Muhteşem fotoğrafımızın" domContentLoaded etkinliğini engellemediğine dikkat edin. Oluşturma ağacını oluşturabilir, hatta sayfadaki her öğeyi beklemeden sayfayı bile boyayabiliriz: Hızlı ilk boyamayı sağlamak için tüm kaynaklar kritik değildir. Hatta kritik oluşturma yolundan bahsettiğimizde genellikle HTML işaretlemesi, CSS ve JavaScript'ten bahsediyoruz. Resimler, sayfanın ilk oluşturulmasını engellemez, ancak resimleri mümkün olan en kısa sürede boyamaya da çalışmalıyız.

Bununla birlikte, load etkinliği (onload olarak da bilinir) resimde engellenmiştir: Geliştirici Araçları, onload etkinliğini 335 ms'de bildirir. onload etkinliğinin, sayfanın gerektirdiği tüm kaynakların indirilip işlendiği noktayı işaret ettiğini unutmayın. Bu noktada, yüklenen döner simgenin tarayıcıda (şelaledeki kırmızı dikey çizgi) dönmesi durabilir.

JavaScript ve CSS'yi karışıma eklemek

"Merhaba Dünya deneyimi" sayfamız basit gibi görünse de arka planda birçok şey olup biter. Pratikte HTML'den daha fazlasına ihtiyacımız olur: Muhtemelen sayfamıza bir miktar etkileşim özelliği eklemek için bir CSS stil sayfamız ve bir veya daha fazla komut dosyanız olacaktır. Bu ikisini de karışıma ekleyip ne olacağını görelim:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Script</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body onload="measureCRP()">
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="timing.js"></script>
  </body>
</html>

Deneyin

JavaScript ve CSS eklemeden önce:

DOM CRP&#39;si

JavaScript ve CSS ile:

DOM, CSSOM, JS

Harici CSS ve JavaScript dosyaları eklemek, şelalemize fazladan iki istek ekler ve bunların hepsi tarayıcı tarafından yaklaşık olarak aynı anda gönderilir. Ancak domContentLoaded ve onload etkinlikleri arasında artık çok daha küçük bir zamanlama farkı olduğunu unutmayın.

Ne oldu?

  • Düz HTML örneğimizin aksine, CSSOM'yi oluşturmak için CSS dosyasını getirip ayrıştırmamız, oluşturma ağacını oluşturmak için ise hem DOM hem de CSSOM'ye ihtiyacımız vardır.
  • Sayfa, ayrıştırıcı engelleyen bir JavaScript dosyası da içerdiğinden, CSS dosyası indirilip ayrıştırılana kadar domContentLoaded etkinliği engellenir: JavaScript, CSSOM'yi sorgulayabileceğinden, JavaScript'i çalıştırabilmemiz için indirilene kadar CSS dosyasını engellememiz gerekir.

Harici komut dosyamızı bir satır içi komut dosyasıyla değiştirirsek ne olur? Komut dosyası doğrudan sayfada satır içine alınsa bile tarayıcı, CSSOM oluşturulana kadar komut dosyasını yürütemez. Kısacası, satır içi JavaScript aynı zamanda ayrıştırıcıyı da engeller.

Bununla birlikte, CSS'de engellemeye rağmen komut dosyasını satır içine almak sayfanın daha hızlı oluşturulmasını sağlar mı? Haydi deneyip sonucu görelim.

Harici JavaScript:

DOM, CSSOM, JS

Satır içi JavaScript:

DOM, CSSOM ve satır içi JS

Bir adet daha az istekte bulunuyoruz, ancak hem onload hem de domContentLoaded süreleri etkin bir şekilde aynı. Neden? JavaScript'in satır içi veya harici olmasının önemli olmadığını biliyoruz, çünkü tarayıcı komut dosyası etiketine ulaştığı anda engel oluyor ve CSSOM oluşturulana kadar bekliyor. Ayrıca, ilk örneğimizde tarayıcı paralel olarak hem CSS hem de JavaScript'i indirir ve indirme işlemini yaklaşık olarak aynı anda bitirir. Bu örnekte, JavaScript kodunu satır içine almak bize çok yardımcı olmaz. Ancak sayfamızın daha hızlı oluşturulmasını sağlayabilecek birkaç strateji var.

Öncelikle, tüm satır içi komut dosyalarının ayrıştırıcı engellemesi olduğunu, ancak harici komut dosyaları için ayrıştırıcının engelini kaldırmak üzere "eş zamansız" anahtar kelimesini ekleyebileceğimizi unutmayın. Satır içi tutma işlemini geri alalım ve deneyelim:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Async</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body onload="measureCRP()">
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script async src="timing.js"></script>
  </body>
</html>

Deneyin

Ayrıştırıcı engelleme (harici) JavaScript:

DOM, CSSOM, JS

Eş zamansız (harici) JavaScript:

DOM, CSSOM, eşzamansız JS

Çok daha iyi. domContentLoaded etkinliği, HTML ayrıştırıldıktan kısa bir süre sonra tetiklenir. Tarayıcı, JavaScript'te engelleme yapmayacağını bilir ve başka ayrıştırıcı komut dosyası olmadığından CSSOM yapısı da paralel olarak devam edebilir.

Alternatif olarak, hem CSS'yi hem de JavaScript'i satır içine alabiliriz:

<!DOCTYPE html>
<html>
  <head>
    <title>Critical Path: Measure Inlined</title>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <style>
      p {
        font-weight: bold;
      }
      span {
        color: red;
      }
      p span {
        display: none;
      }
      img {
        float: right;
      }
    </style>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

Deneyin

DOM, satır içi CSS, satır içi JS

domContentLoaded saatinin önceki örnektekiyle aynı olduğuna dikkat edin; JavaScript'imizi eşzamansız olarak işaretlemek yerine hem CSS'yi hem de JS'yi sayfanın kendisinde satır içine yerleştirdik. Bu, HTML sayfamızı çok daha büyütür, ancak tarayıcının herhangi bir harici kaynağı getirmek için beklemesine gerek olmamasıdır; her şey sayfanın içindedir.

Gördüğünüz gibi, çok basit bir sayfada bile, kritik oluşturma yolunu optimize etmek önemsiz bir işlem değildir: Farklı kaynaklar arasındaki bağımlılık grafiğini anlamamız, hangi kaynakların "kritik" olduğunu belirlememiz ve bu kaynakları sayfaya nasıl dahil edeceğimize ilişkin farklı stratejiler arasından seçim yapmamız gerekir. Bu sorunun tek bir çözümü yoktur; her sayfa farklıdır. Optimum stratejiyi bulmak için benzer bir süreci kendiniz izlemeniz gerekir.

Şimdi geri çekilip bazı genel performans kalıplarını belirleyip belirleyemeyeceğimize bakalım.

Performans kalıpları

Mümkün olan en basit sayfa, yalnızca HTML işaretlemesinden oluşur; CSS, JavaScript veya başka tür kaynaklar içermez. Bu sayfanın oluşturulması için tarayıcının isteği başlatması, HTML belgesinin gelmesini beklemesi, ayrıştırması, DOM'yi oluşturması ve son olarak ekranda oluşturması gerekir:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <title>Critical Path: No Style</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Deneyin

Merhaba dünya CRP

T0 ile T1 arasındaki süre, ağ ve sunucu işlem sürelerini yakalar. En iyi durumda (HTML dosyası küçükse), yalnızca bir ağ döngüsü tüm belgeyi getirir. TCP aktarım protokollerinin çalışma şekli nedeniyle, daha büyük dosyalar daha fazla gidiş-dönüş gerektirebilir. Sonuç olarak, en iyi durumda yukarıdaki sayfada bir döngü (minimum) kritik oluşturma yolu vardır.

Şimdi, aynı sayfayı harici bir CSS dosyasına sahip olduğunu düşünelim:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
  </body>
</html>

Deneyin

DOM + CSSOM CRP

Bir kez daha, HTML belgesini getirmek için bir ağ gidiş dönüşü uygularız ve alınan işaretleme bize CSS dosyasına da ihtiyacımız olduğunu bildirir. Bu da, tarayıcının sayfayı ekranda oluşturabilmesi için önce sunucuya geri gitmesi ve CSS'yi alması gerektiği anlamına gelir. Sonuç olarak, bu sayfa gösterilmeden önce en az iki gidiş-dönüş yapılmalıdır. Bir kez daha, CSS dosyası birden fazla gidiş dönüş alabildiğinden "minimum" vurgulanmalıdır.

Kritik oluşturma yolunu tanımlamak için kullandığımız terimleri tanımlayalım:

  • Kritik Kaynak: Sayfanın ilk oluşturulmasını engelleyebilecek kaynak.
  • Kritik Yol Uzunluğu: Gidiş sayısı veya kritik kaynakların tümünü getirmek için gereken toplam süre.
  • Kritik Baytlar: Sayfanın ilk kez oluşturulması için gereken toplam bayt sayısı. Bu, tüm kritik kaynakların aktarım dosya boyutlarının toplamıdır. Tek bir HTML sayfası içeren ilk örneğimiz, tek bir kritik kaynak (HTML belgesi) içeriyordu. Kritik yol uzunluğu da bir ağ döngüsüne eşitti (dosyanın küçük olduğu varsayılıyordu) ve toplam kritik bayt sayısı sadece HTML belgesinin aktarım boyutu kadardı.

Şimdi bunu yukarıdaki HTML + CSS örneğinin kritik yol özellikleriyle karşılaştıralım:

DOM + CSSOM CRP

  • 2 kritik kaynak
  • Minimum kritik yol uzunluğu için 2 veya daha fazla gidiş dönüş
  • Kritik baytların 9 KB'ı

Oluşturma ağacını oluşturmak için hem HTML'ye hem de CSS'ye ihtiyacımız vardır. Sonuç olarak, hem HTML hem de CSS kritik kaynaklardır: CSS yalnızca tarayıcı HTML dokümanını aldıktan sonra getirilir. Bu nedenle, kritik yol uzunluğu en az iki döngüdür. Her iki kaynağın da toplamda 9 KB'lık kritik bayt toplamı vardır.

Şimdi karışıma fazladan bir JavaScript dosyası ekleyelim.

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

Deneyin

Hem sayfadaki harici JavaScript öğesi hem de ayrıştırıcı (yani kritik) kaynak olan app.js öğesini ekledik. Daha da kötüsü, JavaScript dosyasını çalıştırmak için CSSOM'yi engelleyip beklememiz gerekir. JavaScript'in CSSOM'yi sorgulayabildiğini ve bu nedenle, style.css indirilene ve CSSOM oluşturulana kadar tarayıcının durakladığını unutmayın.

DOM, CSSOM, JavaScript CRP

Bununla birlikte, pratikte bu sayfanın "ağ şelalesine" bakarsak hem CSS hem de JavaScript isteklerinin yaklaşık olarak aynı anda başlatıldığını görürsünüz. Tarayıcı HTML'yi alır, her iki kaynağı da keşfeder ve her iki isteği de başlatır. Sonuç olarak, yukarıdaki sayfa aşağıdaki kritik yol özelliklerine sahiptir:

  • 3 kritik kaynak
  • Minimum kritik yol uzunluğu için 2 veya daha fazla gidiş dönüş
  • Kritik bayt için 11 KB

Şu anda, 11 KB'a kadar kritik bayt toplamına ulaşan üç kritik kaynağımız var. Ancak CSS ve JavaScript'i paralel olarak aktarabildiğimiz için kritik yol uzunluğumuz hâlâ iki döngüden oluşuyor. Kritik oluşturma yolunuzun özelliklerini anlamak hem kritik kaynakları hem de tarayıcının getirme işlemlerini nasıl planlayacağını anlamak anlamına gelir. Örneğimizle devam edelim.

Site geliştiricilerimizle sohbet ettikten sonra, sayfamıza eklediğimiz JavaScript'in engelleme yapmasının gerekmediğini fark ettik; sayfamızın oluşturulmasını engellememesi gerekmeyen bazı analizler ve başka kodlar elimizde bulunmaktadır. Bu bilgiyle, ayrıştırıcının engellemesini kaldırmak için komut dosyası etiketine "async" özelliğini ekleyebiliriz:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Deneyin

DOM, CSSOM, eşzamansız JavaScript CRP

Eşzamansız komut dosyasının birkaç avantajı vardır:

  • Komut dosyası artık ayrıştırıcı engellemez ve kritik oluşturma yolunun bir parçası değildir.
  • Başka kritik komut dosyası olmadığından CSS'nin domContentLoaded etkinliğini engellemesine gerek yoktur.
  • domContentLoaded etkinliği ne kadar erken etkinleşirse diğer uygulama mantığı da o kadar erken yürütülmeye başlar.

Sonuç olarak, optimize edilmiş sayfamız şu anda minimum iki döngülü kritik yol uzunluğuna ve toplam 9 KB kritik bayt sayısına sahip iki kritik kaynağa (HTML ve CSS) geri döndü.

Son olarak, CSS stil sayfası yalnızca yazdırma için gerekli olsaydı bu nasıl olurdu?

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" media="print" />
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

Deneyin

DOM, engellemeyen CSS ve eşzamansız JavaScript CRP

style.css kaynağı yalnızca yazdırma için kullanıldığından, tarayıcının sayfayı oluşturmak için bu kaynağı engellemesi gerekmez. Böylece, DOM oluşturma işlemi tamamlanır tamamlanmaz tarayıcı, sayfayı oluşturmak için yeterli bilgiye sahip olur. Sonuç olarak, bu sayfada tek bir kritik kaynak (HTML dokümanı) vardır ve minimum kritik oluşturma yolu uzunluğu tek bir döngüdür.

Geri bildirim