Kritik Oluşturma Yolu Performansını Analiz Etme

Ilya Grigorik
Ilya Grigorik

Yayınlanma tarihi: 31 Mart 2014

Kritik oluşturma yolu performans darboğazlarını tespit etmek ve çözmek için yaygın hatalardan haberdar olmanız gerekir. Yaygın performans kalıplarını belirlemenize yardımcı olacak rehberli tur, sayfalarınızı optimize etmenize yardımcı olur.

Kritik oluşturma yolunu optimize etmek, tarayıcının sayfayı mümkün olduğunca hızlı bir şekilde boyamasına olanak tanır: Daha hızlı sayfalar, daha yüksek etkileşim, daha fazla görüntülenen sayfa ve dönüşümde artış anlamına gelir. Ziyaretçinin boş ekranı görüntüleme süresini en aza indirmek için hangi kaynakların ve hangi sırayla yükleneceğini optimize etmemiz gerekir.

Bu süreci açıklamak için mümkün olan en basit durumdan başlayın ve sayfamızı ek kaynaklar, stiller ve uygulama mantığı içerecek şekilde kademeli olarak oluşturun. Bu süreçte her destek kaydını optimize ederiz ve nerede sorun yaşanabileceğini görürüz.

Şimdiye kadar, kaynak (CSS, JS veya HTML dosyası) işlenmeye hazır hale geldikten sonra tarayıcıda neler olduğuna odaklandık. Kaynağın önbellekten veya ağdan getirilmesinin ne kadar sürdüğü göz ardı edilmiştir. Aşağıdakileri varsayacağız:

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

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 olmadan temel HTML işaretleme ve tek bir resimle başlayın. Ardından, Chrome Geliştirici Araçları'nda Ağ panelini açın ve ortaya çıkan kaynak şelalesini inceleyin:

CRP

Beklenen gibi, HTML dosyasının indirilmesi yaklaşık 200 ms sürdü. Mavi çizginin şeffaf kısmının, tarayıcının ağda herhangi bir yanıt baytı almadan beklediği süreyi, katı kısmının ise ilk yanıt baytları alındıktan sonra indirme işleminin tamamlanma süresini temsil ettiğini unutmayın. HTML indirme işlemi küçüktür (<4K), bu nedenle dosyanın tamamını almak için tek bir gidiş dönüş işlemi yeterlidir. Sonuç olarak, HTML belgesinin getirilmesi yaklaşık 200 ms sürer. Bu sürenin yarısı ağda, diğer yarısı ise sunucu yanıtını beklerken geçer.

HTML içeriği kullanılabilir hale geldiğinde tarayıcı baytları ayrıştırır, jetonlara dönüştürür ve DOM ağacını oluşturur. DevTools'un, DOMContentLoaded etkinliğinin süresini alt kısımda (216 ms) kolayca raporladığını ve bu sürenin mavi dikey çizgiye de karşılık geldiğini unutmayın. HTML indirme işleminin sonu ile mavi dikey çizgi (DOMContentLoaded) arasındaki boşluk, tarayıcının DOM ağacını oluşturması için geçen süredir. Bu durumda, bu süre yalnızca birkaç milisaniyedir.

"Mükemmel fotoğrafımızın" domContentLoaded etkinliğini engellemediğini görebilirsiniz. Görünüşe göre, sayfadaki her öğeyi beklemeden oluşturma ağacını oluşturabilir ve hatta sayfayı boyayabiliriz: İlk boyamayı hızlı bir şekilde sunmak için tüm kaynakların gerekli olmadığı anlaşılıyor. Aslında, kritik oluşturma yolundan bahsettiğimizde genellikle HTML işaretleme, CSS ve JavaScript'ten bahsederiz. Görseller sayfanın ilk oluşturma işlemini engellemez. Ancak görselleri en kısa sürede oluşturmaya da çalışmalıyız.

Bununla birlikte, load etkinliği (onload olarak da bilinir) resimde engellenir: DevTools, onload etkinliğini 335 ms olarak raporlar. onload etkinliğinin, sayfanın ihtiyaç duyduğu tüm kaynakların indirilip işlendiği noktayı işaret ettiğini unutmayın. Bu noktada, tarayıcıdaki yükleme spinner'ı (şelaledeki kırmızı dikey çizgi) dönmeyi durdurabilir.

JavaScript ve CSS ekleme

"Merhaba Dünya deneyimi" sayfamız basit görünse de arka planda çok şey oluyor. Uygulamada, yalnızca HTML'den daha fazlasına ihtiyacımız olacaktır: Sayfamıza etkileşim eklemek için bir CSS stil sayfasına ve bir veya daha fazla komut dosyasına sahip olmamız olasıdır. Ne olacağını görmek için her ikisini de ekleyin:

<!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

JavaScript ve CSS ile:

DOM, CSSOM, JS

Harici CSS ve JavaScript dosyaları eklemek şelalemize iki ek istek ekler. Bu isteklerin tümü 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?

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

Harici komut dosyamızı satır içi komut dosyasıyla değiştirirsek ne olur? Komut dosyası doğrudan sayfaya yerleştirilmiş olsa bile tarayıcı, CSSOM oluşturulana kadar komut dosyasını yürütemez. Özetle, satır içi JavaScript de ayrıştırıcı engellemedir.

Bununla birlikte, CSS'de engellemeye rağmen komut dosyasının satır içi olarak eklenmesi sayfanın daha hızlı oluşturulmasını sağlar mı? Deneyin ve ne olduğunu görün.

Harici JavaScript:

DOM, CSSOM, JS

Satır içi JavaScript:

DOM, CSSOM ve satır içi JS

Bir istek daha az gönderiyoruz ancak hem onload hem de domContentLoaded sürelerimiz aslında aynı. Neden? JavaScript'in satır içi veya harici olmasının bir önemi olmadığını biliyoruz. Bunun nedeni, tarayıcının komut dosyası etiketine ulaştığı anda engellemesi ve CSSOM oluşturulana kadar beklemesidir. Ayrıca, ilk örneğimizde tarayıcı hem CSS'yi hem de JavaScript'i paralel olarak indirir ve indirme işlemi yaklaşık olarak aynı zamanda tamamlanır. Bu durumda, JavaScript kodunu satır içi olarak yerleştirmek bize pek yardımcı olmaz. Ancak sayfamızın daha hızlı oluşturulmasını sağlayabilecek birkaç strateji vardır.

Öncelikle, tüm satır içi komut dosyalarının ayrıştırıcıyı engellediğini ancak harici komut dosyaları için ayrıştırıcının engellemesini kaldırmak üzere async özelliğini ekleyebileceğimizi hatırlatmak isteriz. Satır içi ekleme işlemimizi geri alıp bunu deneyin:

<!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ıyı engelleyen (harici) JavaScript:

DOM, CSSOM, JS

Asenkron (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'i engellemeyeceğini bilir ve başka ayrıştırıcıyı engelleyen komut dosyası olmadığından CSSOM oluşturma işlemi de paralel olarak devam edebilir.

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

<!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 süresinin önceki örnekle aynı olduğunu fark edin. JavaScript'imizi asenkron olarak işaretlemek yerine hem CSS'yi hem de JS'yi sayfanın içine yerleştirdik. Bu, HTML sayfamızı çok daha büyük hale getirir ancak avantajı, tarayıcının harici kaynakları almak için beklemesi gerekmemesidir. Her şey sayfanın içindedir.

Gördüğünüz gibi, çok basit bir sayfada bile kritik oluşturma yolunu optimize etmek basit 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 dahil etmek için farklı stratejiler arasından seçim yapmamız gerekir. Bu sorunun tek bir çözümü yoktur. Her sayfa farklıdır. Optimum stratejiyi belirlemek için benzer bir süreci kendi başınıza uygulamanız gerekir.

Bununla birlikte, geri çekilip bazı genel performans kalıplarını belirleyip belirleyemeyeceğimize bakalım.

Performans kalıpları

Olası en basit sayfa yalnızca HTML işaretlemesinden oluşur. CSS, JavaScript veya başka tür kaynaklar bulunmaz. Bu sayfayı oluşturmak için tarayıcının isteği başlatması, HTML dokümanının gelmesini beklemesi, dokümanı ayrıştırması, DOM'u oluşturması ve ardından 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) dokümanın tamamı tek bir ağ gidiş dönüş işlemiyle getirilir. TCP aktarım protokollerinin işleyiş şekli nedeniyle, daha büyük dosyalar için daha fazla gidiş geliş gerekebilir. Sonuç olarak, en iyi durumda yukarıdaki sayfanın kritik oluşturma yolu bir gidiş dönüş (minimum) içerir.

Şimdi aynı sayfayı harici bir CSS dosyasıyla ele alalım:

<!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

HTML belgesini almak için tekrar bir ağ gidiş dönüş işlemi gerçekleştiririz. Ardından, alınan işaretleme bize CSS dosyasına da ihtiyacımız olduğunu söyler. Bu, tarayıcının sayfayı ekranda oluşturmadan önce sunucuya geri dönüp CSS'yi alması gerektiği anlamına gelir. Sonuç olarak, bu sayfanın gösterilebilmesi için en az iki gidiş dönüş işlemi gerekir. CSS dosyası birden fazla gidiş dönüş işlemi gerçekleştirebilir. Bu nedenle "minimum" vurgusu yapılmıştır.

Kritik oluşturma yolunu açıklamak için kullandığımız bazı terimler:

  • Kritik Kaynak: Sayfanın ilk oluşturulmasını engelleyebilecek kaynak.
  • Kritik Yol Uzunluğu: Dönüş sayısını veya tüm kritik kaynakları getirmenin toplam süresini belirtir.
  • Kritik Baytlar: Sayfanın ilk oluşturulmasına ulaşmak için gereken toplam bayt sayısıdır. Tüm kritik kaynakların aktarım dosya boyutlarının toplamıdır. Tek bir HTML sayfası içeren ilk örneğimizde tek bir kritik kaynak (HTML dokümanı) vardı. Kritik yol uzunluğu da bir ağ gidiş dönüş işlemine eşitti (dosyanın küçük olduğu varsayılır). Toplam kritik bayt sayısı ise yalnızca HTML dokümanındaki aktarım boyutuydu.

Şimdi bunu önceki HTML ve CSS örneğinin kritik yol özelliklerine karşılaştırın:

DOM + CSSOM CRP

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

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 belgesini aldıktan sonra getirilir. Bu nedenle kritik yol uzunluğu en az iki gidiş dönüştür. Her iki kaynak da toplam 9 KB kritik bayt içerir.

Şimdi buraya ek bir JavaScript dosyası ekleyin.

<!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 bir JavaScript öğesi hem de ayrıştırıcıyı engelleyen (yani kritik) bir kaynak olan app.js öğesini ekledik. Daha da kötüsü, JavaScript dosyasını yürütmek için CSSOM'u engellememiz ve beklememiz gerekir. JavaScript'in CSSOM'u sorgulayabileceğini ve bu nedenle style.css indirilip CSSOM oluşturulana kadar tarayıcının duraklatıldığı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örebilirsiniz. Tarayıcı HTML'yi alır, her iki kaynağı da keşfeder ve her iki isteği de başlatır. Sonuç olarak, önceki resimde gösterilen 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üş
  • 11 KB kritik bayt

Artık 11 KB kritik bayt içeren üç kritik kaynağımız var ancak CSS ve JavaScript'i paralel olarak aktarabildiğimiz için kritik yol uzunluğumuz hâlâ iki gidiş dönüş. Kritik oluşturma yolunuzun özelliklerini anlamak, kritik kaynakları tanımlayabilmek ve ayrıca tarayıcının bu kaynakların getirilmelerini nasıl planlayacağını anlamak anlamına gelir.

Site geliştiricilerimizle görüştükten sonra, sayfamıza eklediğimiz JavaScript'in engellenmesinin gerekmediğini fark ettik. Sayfanın oluşturulmasını engellemesi gerekmeyen bazı analizlerimiz ve başka kodlarımız var. Bu bilgilerle, ayrıştırıcının engellemesini kaldırmak için async özelliğini <script> öğesine 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 dosyalarının birkaç avantajı vardır:

  • Komut dosyası artık ayrıştırıcıyı engellemiyor ve kritik oluşturma yolunun bir parçası değil.
  • Başka kritik komut dosyası olmadığından CSS'nin domContentLoaded etkinliğini engellemesi gerekmez.
  • domContentLoaded etkinliği ne kadar erken tetiklenirse diğer uygulama mantığı o kadar erken yürütülmeye başlayabilir.

Sonuç olarak, optimize edilmiş sayfamız artık iki kritik kaynağa (HTML ve CSS) geri döndü. Kritik yol uzunluğu minimum iki gidiş dönüş ve toplam 9 KB kritik bayttır.

Son olarak, CSS stil sayfasının yalnızca basılı için gerekli olması durumunda bu nasıl görünürdü?

<!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, engellenmeyen CSS ve eşzamansız JavaScript CRP

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

Geri bildirim