Yayınlanma tarihi: 31 Aralık 2013
JavaScript, sayfanın neredeyse her yönünü (içerik, stil ve kullanıcı etkileşimine verdiği yanıt) değiştirmemize olanak tanır. Ancak JavaScript, DOM oluşturulmasını engelleyebilir ve sayfa oluşturulmasını geciktirebilir. En iyi performansı sunmak için JavaScript'inizi eşzamansız hale getirin ve kritik oluşturma yolundan gereksiz JavaScript'leri kaldırın.
Özet
- JavaScript, DOM ve CSSOM'u sorgulayabilir ve değiştirebilir.
- CSSOM'daki JavaScript yürütme blokları.
- JavaScript, açıkça async olarak tanımlanmadığı sürece DOM oluşturmayı engeller.
JavaScript, tarayıcıda çalışan ve sayfanın davranışının neredeyse her yönünü değiştirmemize olanak tanıyan dinamik bir dildir: DOM ağacına öğe ekleyerek ve 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 işlem yapabiliriz. Bunu açıklamak için önceki "Merhaba Dünya" örneğinde kısa bir satır içi komut dosyası eklendiğinde ne olduğuna bakın:
<!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 ulaşmamıza ve gizli span düğümüne referans çekmemize olanak tanır. Düğüm, oluşturma ağacında görünmeyebilir ancak DOM'da hâlâ mevcuttur. Ardından, referans elde ettiğimizde metnini (.textContent aracılığıyla) değiştirebilir ve hatta hesaplanmış görüntüleme stili özelliğini "none" yerine "inline" olarak geçersiz kılabiliriz. Sayfamızda artık "Merhaba etkileşimli öğrenciler!" ifadesi gösteriliyor.
JavaScript, DOM'da yeni öğeler oluşturmamıza, bunlara stil uygulamamıza, eklememize ve kaldırmamıza da olanak tanır. Teknik olarak, sayfamızın tamamı, öğeleri tek tek oluşturan ve stilize eden büyük tek bir JavaScript dosyası olabilir. Bu yöntem işe yarasa da pratikte HTML ve CSS kullanmak çok daha kolaydır. JavaScript işlevimizin ikinci bölümünde yeni bir div öğesi oluşturur, metin içeriğini ayarlar, öğeye stil uygular ve öğeyi body öğesine ekleriz.
Bununla, mevcut bir DOM düğümünün içeriğini ve CSS stilini değiştirdik ve belgeye tamamen yeni bir düğüm ekledik. Sayfamız tasarım ödülü kazanmasa da JavaScript'in bize sunduğu gücü ve esnekliği göstermektedir.
Ancak JavaScript bize çok fazla güç sağlarken sayfanın nasıl ve ne zaman oluşturulacağıyla ilgili birçok ek sınırlama oluşturur.
Öncelikle, önceki örnekte satır içi komut dosyamızın sayfanın alt kısmına yakın olduğunu fark edin. Neden? Bunu kendiniz deneyebilirsiniz. Ancak komut dosyasını <span>
öğesinin üzerine taşırsak komut dosyasının başarısız olduğunu ve dokümanda <span>
öğesine referans bulamadığından şikayet ettiğini görürsünüz. Yani getElementsByTagName('span')
, null
değerini döndürür. Bu, önemli bir özelliği gösterir: Komut dosyamız tam olarak dokümana eklendiği noktada yürütülür. HTML ayrıştırıcı bir komut dosyası etiketiyle karşılaştığında DOM oluşturma işlemini duraklatır ve kontrolü JavaScript motoruna verir. JavaScript motoru çalışmayı bitirdikten sonra tarayıcı kaldığı yerden devam eder ve DOM oluşturmaya devam eder.
Diğer bir deyişle, komut dosyası bloğumuz henüz işlenmediği için sayfanın sonraki bölümlerindeki öğeleri bulamıyor. Başka bir ifadeyle, satır içi komut dosyamızın çalıştırılması DOM oluşturma işlemini engeller ve bu da ilk oluşturma işlemini geciktirir.
Sayfamıza komut dosyası eklemenin bir diğer ince özelliği, komut dosyalarının yalnızca DOM'u değil, CSSOM özelliklerini de okuyup değiştirebilmesidir. Aslında, örnekte span öğesinin display özelliğini none yerine inline olarak değiştirdiğimizde tam olarak bunu yapıyoruz. Peki nasıl bir sonuç alacaksınız? Artık bir yarışma durumu var.
Komut dosyamızı çalıştırmak istediğimizde tarayıcı CSSOM'u indirip oluşturmayı tamamlamamışsa ne olur? Yanıt, performans açısından pek iyi değil: Tarayıcı, CSSOM'u indirip oluşturmayı tamamlayana kadar komut dosyası yürütme ve DOM oluşturma işlemlerini geciktirir.
Özetle, JavaScript; DOM, CSSOM ve JavaScript yürütme arasında birçok yeni bağımlılık sunar. Bu durum, tarayıcının sayfayı işleme ve ekranda oluşturma konusunda önemli gecikmelere neden olabilir:
- Komut dosyasının dokümanda bulunduğu konum önemlidir.
- Tarayıcı bir komut dosyası etiketiyle karşılaştığında, komut dosyası yürütülmeyi tamamlayana kadar DOM oluşturma duraklatılır.
- JavaScript, DOM ve CSSOM'u sorgulayabilir ve değiştirebilir.
- CSSOM hazır olana kadar JavaScript yürütme 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ğini anlama ve optimize etmeyi ifade eder.
Ayrıştırıcı engelleme ve eşzamansız JavaScript
Varsayılan olarak, JavaScript yürütme "ayrıştırıcıyı engelleme" şeklindedir: Tarayıcı, belgede bir komut dosyasıyla karşılaştığında DOM oluşturma işlemini duraklatmalı, kontrolü JavaScript çalışma zamanına devretmeli ve DOM oluşturma işlemine devam etmeden önce komut dosyasının yürütülmesine izin vermelidir. Bu yöntemi, önceki örneğimizde bir satır içi komut dosyasında gördük. Aslında, satır içi komut dosyaları, yürütülmesini ertelemek için ek kod yazmadığınız sürece her zaman ayrıştırıcı engeller.
Komut dosyası etiketi kullanılarak eklenen komut dosyaları ne olacak? Yukarıdaki örneği ele alalım ve kodu ayrı bir dosyaya çıkarın:
<!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);
<script> etiketi veya satır içi JavaScript snippet'i kullanıp kullanmadığımıza bakılmaksızın, her ikisinin de aynı şekilde davranmasını beklersiniz. Her iki durumda da tarayıcı, dokümanın geri kalanını işleyemeden önce komut dosyasını duraklatıp yürütür. Bununla birlikte, harici bir JavaScript dosyası söz konusu olduğunda, komut dosyasının diskten, önbellekten veya uzak bir sunucudan getirilmesini beklemek için tarayıcının duraklaması gerekir. Bu durum, kritik oluşturma yolunda onlarca milisaniyelik gecikmelere neden olabilir.
Varsayılan olarak tüm JavaScript'ler ayrıştırıcı tarafından engellenir. Tarayıcı, komut dosyasının sayfada ne yapmayı planladığını bilmediği için en kötü durum senaryosunun geçerli olduğunu varsayar ve ayrıştırıcıyı engeller. Tarayıcıya, komut dosyasının tam olarak referans verildiği noktada yürütülmesi gerekmediğine dair bir sinyal gönderildiğinde tarayıcı, DOM'u oluşturmaya devam eder ve komut dosyası hazır olduğunda (ör. dosya önbellekten veya uzak bir sunucudan getirildikten sonra) komut dosyasının yürütülmesine izin verir.
Bunu yapmak için async
özelliği <script>
öğesine eklenir:
<!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>
Eş zamansız anahtar kelimenin komut dosyası etiketine eklenmesi, komut dosyasının kullanılabilir hale gelmesini beklerken tarayıcıya DOM oluşturma işlemini engellememesini bildirir. Bu, performansı önemli ölçüde artırabilir.