JavaScript, sayfanın hemen hemen her yönünü değiştirmemize olanak tanır: içerik, stil ve kullanıcı etkileşimine verdiği yanıt. Ancak JavaScript, DOM oluşturma işlemini de engelleyebilir ve sayfa oluşturulurken gecikme yapabilir. En iyi performansı sunmak için JavaScript'inizi eşzamansız hale getirin ve gereksiz JavaScript'leri kritik oluşturma yolundan çıkarın.
Özet
- JavaScript, DOM ve CSSOM'yi sorgulayabilir ve değiştirebilir.
- CSSOM'deki JavaScript yürütme blokları.
- JavaScript, eşzamansız olduğu açıkça bildirilmediği sürece DOM oluşturma işlemini engeller.
JavaScript, tarayıcıda çalışan dinamik bir dildir ve sayfanın davranışını neredeyse her açıdan değiştirmemize olanak tanır: DOM ağacından öğe ekleyip kaldırarak içeriği değiştirebilir, her öğenin CSSOM özelliklerini değiştirebilir, kullanıcı girişini işleyebilir ve daha pek çok şey yapabiliriz. Bunu açıklamak için önceki "Hello World" örneğimizi basit bir satır içi komut dosyasıyla geliştirelim:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
<title>Critical Path: Script</title>
</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>
JavaScript, DOM'a erişmemizi ve gizli aralık düğümüne yönelik referansı çekmemizi sağlar. DOM, oluşturma ağacında görünmeyebilir ancak yine de DOM'de yer almaktadır. Referansı edindiğimizde, referans metnini değiştirebiliriz (.textContent aracılığıyla) ve hatta hesaplanan görüntüleme stili özelliğini "yok" yerine "satır içi" olarak değiştirebiliriz. Şimdi sayfamızda "Merhaba etkileşimli öğrenciler!" ifadesi görüntüleniyor.
JavaScript, DOM'de yeni öğeler oluşturmamıza, stilini eklememize, eklememize ve kaldırmamıza da olanak tanır. Teknik olarak, sayfamızın tamamı öğeleri tek tek oluşturan ve biçimlendiren büyük bir JavaScript dosyasından oluşabilir. Bu işe yarasa da pratikte HTML ve CSS kullanmak çok daha kolaydır. JavaScript fonksiyonumuzun ikinci bölümünde, yeni bir div öğesi oluşturur, metin içeriğini ayarlar, stilini belirler ve gövdeye ekleriz.
Bununla birlikte, mevcut bir DOM düğümünün içeriğini ve CSS stilini değiştirdik ve dokümana tamamen yeni bir düğüm ekledik. Sayfamız herhangi bir tasarım ödülü kazanmayacak ancak JavaScript'in bize sağladığı gücü ve esnekliği gösteriyor.
Ancak, JavaScript bize çok fazla güç verse de, sayfanın nasıl ve ne zaman oluşturulacağına ilişkin birçok ek sınırlama oluşturur.
Öncelikle, yukarıdaki örnekte satır içi komut dosyamızın sayfanın alt kısmına yakın olduğuna dikkat edin. Neden? Kendiniz denemeniz gerekir, ancak komut dosyasını span öğesinin üzerine taşırsak komut dosyasının başarısız olduğunu fark eder ve dokümandaki hiçbir span öğesine başvuru bulamadığından şikayet eder. Yani, getElementsByTagName(‘span') işlevi null değerini döndürür. Bu, önemli bir özelliği gösterir: Komut dosyamız tam olarak dokümanda eklendiği noktada çalıştırılır. HTML ayrıştırıcısı bir komut dosyası etiketiyle karşılaştığında, DOM'u oluşturma sürecini duraklatır ve kontrolü JavaScript motoruna verir. JavaScript motoru çalışmayı bitirdikten sonra, tarayıcı kaldığı yerden devam eder ve DOM oluşturma işlemine devam eder.
Diğer bir deyişle, komut dosyası bloğumuz henüz işlenmedikleri için sayfanın sonraki bölümlerinde herhangi bir öğe bulamaz! Biraz daha farklı ifade etmek gerekirse: Satır içi komut dosyamızı yürütmek DOM oluşturma işlemini engeller ve bu da ilk oluşturma işleminin gecikmesine neden olur.
Sayfamıza komut dosyaları sunmanın bir diğer ince özelliği de yalnızca DOM'yi değil, CSSOM özelliklerini de okuyup değiştirebilmeleridir. Aslında, örneğimizde span öğesinin display özelliğini "yok" yerine satır içi olarak değiştirdiğimizde tam olarak bunu yapıyoruz. Peki nasıl bir sonuç alacaksınız? Artık bir yarış durumumuz var.
Komut dosyasını çalıştırmak istediğimizde tarayıcı CSSOM'yi indirmeyi ve oluşturmayı tamamlamadıysa ne olur? Bu sorunun yanıtı basittir ve performans açısından pek iyi değildir: Tarayıcı, CSSOM'yi indirme ve oluşturma işlemi tamamlanana kadar komut dosyasının yürütülmesini ve DOM oluşturma işlemini geciktirir.
Kısacası JavaScript; DOM, CSSOM ve JavaScript'in yürütülmesi arasında birçok yeni bağımlılık getirir. Bu durum, tarayıcının sayfayı işlemesinde ve ekranda oluşturmasında önemli gecikmelere neden olabilir:
- Komut dosyasının dokümandaki konumu önemli.
- Tarayıcı bir komut dosyası etiketiyle karşılaştığında, komut dosyasının yürütülmesi bitene kadar DOM oluşturma işlemi duraklatılır.
- JavaScript, DOM ve CSSOM'yi sorgulayabilir ve değiştirebilir.
- CSSOM hazır olana kadar JavaScript yürütme işlemi duraklatılır.
"Kritik oluşturma yolunu optimize etme" büyük ölçüde HTML, CSS ve JavaScript arasındaki bağımlılık grafiğinin anlaşılması ve optimize edilmesi anlamına gelir.
Ayrıştırıcı engelleme ile eşzamansız JavaScript karşılaştırması
Varsayılan olarak, JavaScript'in çalıştırılması "ayrıştırıcı engelleme" şeklindedir: Tarayıcı, dokümanda bir komut dosyasıyla karşılaştığında DOM yapısını duraklatmalı, kontrolü JavaScript çalışma zamanına devretmeli ve DOM oluşturmaya devam etmeden önce komut dosyasının çalışmasına izin vermelidir. Önceki örneğimizde bunun bir satır içi komut dosyasıyla nasıl çalıştığını gördük. Aslında, yürütmelerini ertelemek için ek kod yazmadığınız sürece satır içi komut dosyaları her zaman ayrıştırıcı engellemesi olur.
Komut dosyası etiketi aracılığıyla eklenen komut dosyaları ne olacak? Önceki örneğimizi alıp kodu ayrı bir dosyaya çıkaralım:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
<title>Critical Path: Script External</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<script src="app.js"></script>
</body>
</html>
app.js
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);
İster <script> etiketi ister satır içi JavaScript snippet'i kullansak her ikisinin de aynı şekilde davranmasını beklersiniz. Her iki durumda da, tarayıcı dokümanın geri kalanını işlemeden önce komut dosyasını duraklatır ve yürütür. Ancak harici bir JavaScript dosyası söz konusu olduğunda, tarayıcının komut dosyasının diskten, önbellekten veya uzak bir sunucudan getirilmesini beklemek için duraklaması gerekir. Bu da, kritik oluşturma yoluna on binlerce milisaniyelik gecikme ekleyebilir.
Varsayılan olarak tüm JavaScript ayrıştırıcı engelleme özelliğine sahiptir. Tarayıcı, komut dosyasının sayfada ne yapmayı planladığını bilmediğinden, en kötü senaryoyu varsayar ve ayrıştırıcıyı engeller. Tarayıcıya, komut dosyasının başvuruda bulunduğu tam noktada yürütülmesi gerekmediğini belirten bir sinyal gönderilir. Bu sinyal, tarayıcının DOM'u oluşturmaya devam etmesine ve komut dosyasının hazır olduğunda (örneğin, dosya önbellekten veya uzak bir sunucudan alındıktan sonra) çalışmasına olanak tanır.
Bunu sağlamak için komut dosyamızı async olarak işaretleriz:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
<title>Critical Path: Script Async</title>
</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>
Komut dosyası etiketine eşzamansız anahtar kelimenin eklenmesi, tarayıcıya, komut dosyasının kullanılabilir hale gelmesini beklerken DOM oluşturma işlemini engellememesini bildirir. Bu durum, performansı önemli ölçüde artırabilir.