Aslında back-end 🔩 tarafında kod yazdığım için javascript dilinde, temelleri 🎯 ve çok bilinen frameworkleri dışında şahsen öyle aman aman bir bilgim ve kullanımım yok. Gerçi yıllar önce Chrome V8 engine bu durumu tepetaklak etti. Tarayıcıya ihtiyaç duymadan sunucu tarafına da müdahale ettiği için bir backend dili olmaya doğru evrildi. Yanı sıra implements, interface, instanceof, private, public gibi aşina olunan erişim belirleyicileri revize edilmiş durumda. Bu yüzden bence 'hybrid' bir dil haline geldi.

Yani js uzun süre daha yerini koruyacağa benziyor. Bu yüzden bugün çok işe yarar püf noktalarının birinden bahsetmek istiyorum. 'External' kullanımlar için yani <script src="script.js" /> ya da <script src="//google.com/script.js"/> gibi domain dışında bir kullanımda geçilebilecek iki argümandan bahsedeceğim: async ve defer. Aslında bilindik js frameworkler bahsedeceğim işlemleri optimize ediyorlar; ancak özellikle diğer 3.parti kütüphanelerin kullanımı veya özellikle vanilla.js yazarken bu kullanımların önemi ortaya çıkar. Aslında bu iki attribute tarayıcıların kodları 'execute' etme şeklini kısmen değiştiriyor. Bu yazıda javascript nasıl çalışır, statik olmayan bir sayfa 'nasıl ayağa kalkar', DOM manipulation nedir gibi konulara değinmeyeceğim. Gereksiz yere akıl karıştırmak ve yazıyı şişirmek istemiyorum. Ama bunları araştırmanızı tavsiye ederim.

Şu an için bu iki attribute inline kullanılırsa derleyici herhangi bir hata vermez ancak HTML parse veya scriptler için bir etkisi olmaz.

Önce bu iki argüman kullanılmadan sayfanın nasıl ilerlediğine bakalım.

<script>

Aşağıdaki grafikte göreceğiniz gibi script etiketi ile karşılaşılana kadar HTML parse edilmeye devam edilir; ancak etiketle karşılaşılınca parse işlemi bloklanır ve script indirilerek çalıştırılmaya başlanır.

legend

legend

Günümüzün modern web uygulamalarında script dosyaları HTML satırlarından daha yüklü satırlar içerdiği için boyutları çok daha fazladır. Bu yüzden eskiye nazaran, kendi alan adınız ya da 3.parti bir script dosyasının indirilerek işlenmesi daha uzun sürer. Normalde tarayıcı HTML elementlerini yüklerken <script src="... /> gördüğü satırda dosyanın indirilmesini bekler ancak script indirilip çalıştırıldıktan sonra sayfanın geri kalanını parse etmeye devam edebilir. Yani script etiketinden sonraki tüm satırlar bloklanır. Bu, temelde iki önemli soruna yol açar.

  • Sayfada script dosyasından sonraki DOM elementleri ile ilgili bir işlem yapılamaz.
  • Eğer script boyutu çok büyükse sayfa yüklenene kadar HTML bloklanacaktır.
<!doctype html>
<body>
<p id="first-p">Bu satır script tagından önce</p>

<script src="script.js" />

<!-- Bu satır script dosyası yüklenene kadar gözükmez -->
<p id="second-p">Bu satır script tagından sonra</p>
</body>

Bu sorun window.onload event'e function geçilerek ya da script sayfanın en altına eklenerek aşılmaya çalışılır. Fakat HTML parse işleminin nispeten uzun sürdüğü durumlarda da ilgili fonksiyon çıktısı için önce HTML'in tüm işinin bitirilmesini DOMContentLoaded event bekleyecektir. Yani yapılan bu işlem, sorunun temelini çözmekten uzaktır. Hızlı bağlantılarda sorun çok fark edilmeyebilir ama özellikle yavaş hızlarda bu olay çok fazla göze batar hale gelir.

<script defer>

legend

legend

Bu attribute tarayıcıya script etiketini görse de HTML okumaya devam etmesini bildirir. Aynı sırada script dosyası arka planda indirilir ve HTML tamamen oluşurulduğunda çalışır. Yukarıdaki örneği defer ile deneyelim.

<!doctype html>
<body>
<p id="first-p">Bu satır script tagından önce</p>

<script defer src="script.js" />

<!-- Bu satır script dosyasını beklemeden görünür hale gelecektir-->
<p id="second-p">Bu satır script tagından sonra</p>
</body>
  • Sayfa bloklanmadan HTML işlenmeye devam eder.
  • Script dosyası, DOM hazır olduğunda, DOMContentLoaded event öncesinde çalıştırılır.
<!doctype html>
<body>
<p id="first-p">Bu satır script tagından önce</p>

<script>
     document.addEventListener('DOMContentLoaded', () => console.log("defer sonrası hazır DOM"));
</script>

<script defer src="script.js" />

<!-- Bu satır script dosyasını beklemeden görünür hale gelecektir-->
<p id="second-p">Bu satır script tagından sonra</p>
</body>

Bu örnekte de sayfa bloklanmadan görünür. DOMContentLoaded event handler defer attribute ile işaretlenmiş olan script dosyasını bekler. Bu dosya indirilip çalıştırıldıkdan sonra tetiklenecektir.

Ayrıca bu attribute kullanan farklı external dosyalar normal sıralarını koruyarak işleme alınır ki bu da çok işe yarar bir durumdur. Modern tarayıcılar, performans için tüm sayfayı tarayarak script etiketlerini aynı anda indirmeye başlarlar ve script dosyaları birbirini bloklamadan işlemlerine devam edecektir. Ancak defer kullanımı; tarayıcıya dosyanın HTML'i bloklamamasını bildirmenin dışında, dosyaların normal sırasını da koruması gerektiğini bildirir.

<script defer src="long.js" />
<script defer src="small.js" />

Yani yukarıdaki örnekte önce long.js, daha sonra small.js indirilecektir. Eğer defer kullanmasaydık small.js alt satırda olmasına rağmen boyutu daha küçük olduğu için daha erken indirilecek ve eğer long.js içinde kullanacağı bir component varsa hata verecektir.

<script async>

legend

legend
  • defer ile benzer şekilde bu attribute kullananan script etiketleri HTML'i bloklamaz.
  • Diğer script dosyaları async ile işaretlenen scriptleri beklemediği gibi async ile işaretlenen scrpitler de diğerlerini beklemez.
  • DOMContentLoaded event ve async scriptler de birbirlerini beklemez. Event async dosyasından önce de sonra da tetiklenebilir. Yani async script boyutu ile ters orantılı olarak çalışır.
  • Sayfa içeriği async ile HTML bloklanmadan görünür kalacaktır.
  • DomContentLoaded event öncesi veya sonrası işleme alınabilir. Yine aynı şekilde büyük boyutlu dosya eğer tarayıcıda önbelleklenmiş ise; örneğin ilk çalıştırmada small.js ikinci çalıştırmada long.js önce çalışır. Haliyle hangisinin önce olacağı ile ilgili kesin bir garantisi yoktur. Eee nolmuş yani ne farkeder ki 😄 Fark şu; 1 saniye arayla yaptığınız iki test tamamen farklı da çıkabilir, aynı da çıkabilir. Bu aslında javascript özelinde değil; asenkron programlamayla az-çok uğraştığınızda, eğer düzgün bir yapı kurmazsanız çalışma sürelerine göre iş parçacıklarının nasıl birbirine girerek insanı delirtebilecek hale gelmesiyle ilgili bence. 😵 O yüzden benzer işlemlerde geliştirme konsolunda Network > Disable cache şekilde çalışmak mantıklı olacaktır.

Yani async attribute kullanıldığında HTML bloklanmaz fakat defer gibi HTML oluşmasını beklemeden aynı anda asenkron şekilde script dosyasını da indirecektir.

<!doctype html>
<body>
<p id="first-p">Bu satır script etiketinden önce</p>

<script>
     document.addEventListener('DOMContentLoaded', () => console.log("defer sonrası hazır DOM"));
</script>

<script async src="long.js" />
<script async src="small.js" />

<!-- Bu satır script dosyasını beklemeden görünür hale gelecektir-->
<p id="second-p">Bu satır script etiketinden sonra</p>
</body>

Önceki örnekte defer kullandığımız için dosya sırası korunurken, burada dosyalar asenkron şekilde indirilip çalıştırılmaya başlayacak ve small.js boyutu küçük olduğu için muhtemelen daha önce çalışacaktır.

Özellikle 3.parti scriptlerin çoğunun dökümanında bu async attribute zaten yer alır. Mesela Google Analytics sayfanın görünürlüğü ile bir ilgisi olmadığı için asenkron şekilde HTML ile birlikte arkada inecek ve performansı gereksiz yere yormayacaktır.

Sonuç olarak; her iki attribute için de asıl amaç script indirilirken sayfayı bloklamamaktır.

#SıralamaDOMContentLoaded
asyncscript sırası önemli olmadan ilk indirilen önce çalışırHTML'in ya da scriptin boyutuna göre çalışma sırası ile ilgili bir kesin bir garantisi yoktur. Boyutu küçük olan önce çalışır.
deferscript sırası bildirilen sıraya göre indirilerek çalıştırılırHTML tamamen yüklenip parse edildikten sonra bu event öncesinde çalışır.

Hangisini, Nerede, Ne Zaman Kullanalım?

Eee madem bu kadar avantajı var o zaman tüm scriptlere async ya da defer verelim gitsin... mi?🤔Bu tamamen projedeki önceliklerinize bağlı. Yani bu iki attribute kullanıldığı zaman scriptler indirip çalıştırılmadan sayfa kullanıcıya gösterilir; scriptler ile sayfada yapacağınız ayarlamalar henüz sayfada kullanılabilir durumda değildir. Bu durumda script indirilip çalıştırılana kadar sayfada görsel bozulmalar, kaymalar meydana gelecek, daha sonra toparlayacaktır.

Arama motorları, bir siteye geldiğinde bizim gözle gördüğümüz gibi yorumlamayacaktır. Yani daha sade bir dille; arama motoru dediğimiz milisaniyelik 'ziyaretçi' sadece koddan anlar ve aslında bizim gözümüze hoş gelen her animasyon onu koda odaklanmaktan alıkoyacağı için bundan pek hoşlanmaz. O yüzden en hızlı ve SEO dostu siteler kupkuru HTML çıktıları olan sitelerdir. Mesela ben, her ne kadar kupkuru bir HTML en iyisidir desem de bu sitede bir sürü animasyona izin verdim ancak temel script dosyam için bile bu argümanları kullandım çünkü performans tarafını öne çekmek istedim. Bu yüzden bazen sayfanın ilk açılışında toparlanmasını gözle fark edebilirsiniz. Bazen sitelerin yüklendiğini bildiren loading animasyonları bunu gizlemek için kullanılır. Bu yüzden şu yol doğrudur diye bir şey olmaz, ben sadece kullanımlarından bahsettim. Bunun kararını hız & erişilebilirlik & görsellik gibi ihtiyaçların kesişimine göre, proje özelinde kendiniz vermelisiniz.👍