Svelte Nedir? React ve Vue’nün Yerini Alabilir mi?
Svelte (sıvelt), kelime anlamı olarak ince yapılı, ve fidan gibi zarif anlamları vardır. Svelte framework’ünün amacı ise yüksek performanslı web uygulamalarının geliştirilmesini sağlamaktır. Component’ler ile kolay bir şekilde akıcı ve interaktif kullanıcı arayüzleri sağlama amacı taşıdığı için React ve Vue gibi JavaScript framework’leri arasında oldukça benzerlik gösterir. Ancak diğerlerinin uygulamayı runtime‘da yorumlamasının aksine Svelte, derleme zamanında ideal JavaScript kodunu oluşturur. Bu sayede framework’ün soyutlamalarının getirdiği performans maliyetinden kurtulmuş olur ve uygulama ilk yüklendiği esnada oluşan gereksiz iş yüküne maruz kalmaz. Peki gömülü cihazlarda dahi çalışabilen Svelte’i diğer framework’lerden ayıran özellikler nelerdir?
Daha az kod yazmanızı sağlar
Çalışan her mekanik birim, ne kadar çok özellikli olursa, o kadar sorun verme ihtimali artar. Yazılım dünyasında da durum benzer şekildedir. Bu nedene ne kadar çok kod yazarsanız o kadar hata oluşma ihtimali artar. Ayrıca uzun kodun yazımı daha fazla zaman aldığı için, uygulama optimizasyonu ve uygulamaya eklenecek güzel özellikler için daha az zaman ayrılıyor.
Birçok geliştiricinin de bildiği gibi bir projenin gelişim zamanı ve oluşacak bug sayısı, proje kodunun büyüklüğü ile karesel orantılıdır. Buradan yola çıkarsak, 10 satırlık bir kod, 100 satırlık olana göre çok daha az kafa yorucu olacaktır. Ayrıca bir dosya içerisindeki kod miktarı arttıkça kodu okumak için scroll yapmak gerekecek ve yukarı-aşağı gidişlerle kodun anlaşılabilirliği önemli ölçüde azalacaktır. Bu nedenle geliştirici yorum satırı ekleyecek ve oluşan kod miktarı daha da artacaktır. Bu nedenle Uncle Bob’un da dediği gibi dosya başına 200 satırlık bir limitin konulması genellikle iyidir.
Fakat buna rağmen (haklı olarak) yazdığımız kod miktarına bakmak yerine, kullanıcıyı daha çok etkileyebilecek olan performans ölçümüne, oluşan JS paketinin büyüklüğüne ve diğer ölçebileceğimiz parametrelere yoğunlaşırız.
Kodun okunabilirliği önemlidir
Elbette, kodu sıkıştırarak olabilecek en kompakt forma dönüştürmek veya uygulama içerisindeki kod satırı miktarını azaltmak nihai amacımız olmamalıdır. Çünkü aşağıdaki gibi çift sayıları yazdıran okunabilir bir kodun:
for (let i = 0; i <= 100; i += 1) {
if (i % 2 === 0) {
console.log(`${i} çifttir.`);
}
}
tek satırlık semboller silsilesi haline gelmesi de okunabilirliği azaltacaktır:
for (let i = 0; i <= 100; i += 1) if (i % 2 === 0) console.log(`${i} çifttir.`);
Bunun yerine okunabilirlik için, kullandığımız programlama dilindeki özellikleri ve şablonları uygulayarak organik bir şekilde daha az miktarda kod yazmalıyız.
Svelte ve az miktarda kod ile uğraşmak
Yazılacak kod miktarının azaltılması, Svelte’in başlıca amaçlarından biridir. Bunun için öncelikle basit bir bileşenin React, Vue ve Svelte ile nasıl kodlandığına bakalım:
<script>
let a = 1;
let b = 2;
</script>
<input type=“number” bind:value={a}>
<input type=“number” bind:value={b}>
<p>{a} + {b} = {a + b}</p>
Peki bu bileşeni React’te hook’lar ile yapmak istediğimizde nasıl bir kod ortaya çıkacaktı?
import React, { useState } from ‘react’;
export default () => {
const [a, setA] = useState(1);
const [b, setB] = useState(2);
function handleChangeA(event) {
setA(+event.target.value);
}
function handleChangeB(event) {
setB(+event.target.value);
}
return (
<div>
<input type=“number” value={a} onChange={handleChangeA}/>
<input type=“number” value={b} onChange={handleChangeB}/>
<p>{a} + {b} = {a + b}</p>
</div>
);
};
Aynı bileşenin Vue’deki karşılığı da aşağıdaki gibi olacaktır:
<template>
<div>
<input type=“number” v-model.number=“a”>
<input type=“number” v-model.number=“b”>
<p>{{a}} + {{b}} = {{a + b}}</p>
</div>
</template>
<script>
export default {
data: function() {
return {
a: 1,
b: 2
};
}
};
</script>
Oluşan kod miktarlarını karakter bazlı karşılaştıracak olursak:
- React: 442
- Vue: 263
- Svelte: 145
Üstte de görüldüğü gibi React kodu, Svelte ile yazılanın 3 katı karakter sayısına sahiptir. Tabi her bileşen için bu kadar uç bir fark oluşmasa da, React ile yazılan kodun Svelte’dekine göre daha fazla olduğu gerçeğini değiştirmemektedir.
Svelte’in, daha az kod yazmamızı sağlayacak özelliklerine değinelim.
Üst seviye elementler
Bildiğiniz üzere React ve Vue’de yalnızca bir adet üst-seviye element oluşturulabiliyor. Eğer birden fazla varsa genellikle bir <div>
veya <React.Fragment>
ile sarmalanarak return ediliyor. Gereksiz yere ekstra olarak bir tab boşluk oluşmuş hale geliyor. Vue’de ise elemanlar <template>
ile gereksiz bir şekilde sarmalanması gerekiyor.
Svelte’de ise, aşağıdaki gibi bir sarmalama gerekmeden istediğiniz kadar üst seviye element oluşturabilirsiniz:
<input type=“number” bind:value={a}>
<input type=“number” bind:value={b}>
<p>{a} + {b} = {a + b}</p>
State’in Yönetimi
Svelte’de bir bileşenin yerel state’i eşittir (=) atama operatörü ile yönetilir:
let count = 0;
function increment() {
count += 1;
}
React’te ise useState
hook’u ile bu kod yazılabilir:
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
}
Svelte ile kıyaslandığında React’teki karşılığı daha karmaşık duruyor. İkisi de aynı işi yapmasına rağmen React’te %60 daha fazla karakter kullanılıyor. Bu nedenle kodun yaptığı işi anlamak için daha fazla çaba sarfedilmesi gerekiyor.
Vue’de ise, yerel state’e karşılık gelen nesne içeriğini geri döndüren bir data
fonksiyonu bulunuyor. Yardımcı fonksiyonlar ve alt bileşenler import edilerek template içerisinde kullanılamadığı için, export edilen Vue nesnesine iliştirerek kullanılmak durumda oluyor.
Boilerplate kodun bulunmaması
Svelte’in, oluşabilecek minimum özel kod ile kullanıcı arayüzleri oluşturmayı sağlayacağı birçok yöntem vardır. Örneğin Svelte’deki reaktif deklarasyonlar, React’teki useMemo
, useCallback
ve useEffect
gibi boilerplate kodun oluşmasını engeller. Bununla birlikte, her state değişikliğinde, inline fonksiyonlar ve array’lerin oluşturulmasından dolayı ortaya çıkan garbage collection yükü oluşmaz.
Peki nasıl daha az kod ile bu kadar iş yapılabiliyor? Çünkü Svelte, bir derleyici olduğundan dolayı JavaScript’in tuhaflıklarının esiri olmak zorunda değildir. Bu nedenle, JavaScript dilinin semantiği arasında sıkışıp kalmak yerine, yazılımcı için bileşen yapma deneyimi tasarlanmıştır. Böylece proxy veya hook yerine JavaScript dilinin yeteneklerinden faydalanarak değişken kullanımının öne çıktığı yüksek performanslı uygulamalar üretilebilir hale gelmektedir.
Virtual DOM Gerçekten Hızlı mıdır?
Eğer son zamanlarda JavaScript framework’leri ile ygulama geliştiriyorsanız, muhtemelen virtual DOM’u duymuşsunuzdur. virtual DOM’un hızlı olduğu, hatta gerçek DOM işlemlerinden daha hızlı çalıştığı söylenir. Bu fikir o kadar yaygınlaşmıştır ki Svelte için de “Virtual DOM kullanmıyor, o halde nasıl hızlı olabilir ki?” sorusunu akıllara getirmiştir. Bu soruyu cevaplamak için öncelikle virtual DOM’un ne olduğuna daha yakından bakalım
Virtual DOM nedir?
Birçok framework’te render()
fonksiyonunun içini doldurarak uygulamalar geliştirebilirsiniz. Örneğin aşağıdaki React örneğini ele alalım:
function HelloMessage(props) {
return (
<div className=“greeting”>
Hello {props.name}
</div>
);
}
Bunu JSX söz dizimi kullanmadan da aşağıdaki gibi gerçekleştirebilirsiniz:
function HelloMessage(props) {
return React.createElement(
‘div’,
{ className: ‘greeting’ },
‘Hello ‘,
props.name
);
}
Her iki durumda da, web sayfasının nasıl görüneceğini tasvir eden bir nesne üretilecektir. İşte üretilen bu nesneye Virtual DOM adı verilir. Uygulamanın state’i her güncellendiğinde (örneğin name
prop’u değiştiğinde), yeni bir virtual DOM üretilir. React de yeni oluşturulan virtual DOM’u eskisiyle karşılaştırarak hangi kısımların değiştiğini bulur ve gerçek DOM’a bu değişikliği uygular.
Peki virtual DOM’un daha hızlı olduğu söylentisi nasıl ortaya çıktı?
Virtual DOM’un performansı ile ilgili yanlış anlaşılmalar aslında React’in ortaya çıktığı tarihe kadar uzanıyor. 2013 yılında düzenlenen JSConfEU etkinliğinde, Rethinking Best Practices sunumunda React ekibinin eski üyesi olan Pete Hunt aşağıdaki konuşmasını aktarmıştı:
Virtual DOM oldukça hızlıdır. Çünkü DOM işlemlerinin çoğu yavaş olma eğilimindedir. DOM üzerinde performans iyileştirmeleri yapılıyor fakat buna rağmen DOM işlemlerinin çoğunda FPS düşmektedir.
Bu fikir ile virtual DOM’un ne olduğu bilgisini bir araya getirdiğimizde, aslında virtual DOM’un da nihai olarak gerçek DOM üzerinde iş yaptığını görürüz. Daha hızlı olabilmesi için tek neden o dönemde (ki 2013’te Angular dahil birçok JS framework’ü bulunuyordu) daha az performanslı bir framework’ün kıyaslanmasıdır. Ya da aşağıdaki gibi bir kod parçacığının daha hızlı çalıştığını savunmaktır:
onEveryStateChange(() => {
document.body.innerHTML = renderMyApp();
});
Pete bu slayttan hemen sonra sözlerine aşağıdaki şekilde açıklama getirmiştir:
React bir sihir değildir. Nasıl ki C derleyicisini bırakıp C kodları ve assembler ile çalışabileceğiniz gibi, React’i bırakıp DOM Api çağrıları ve DOM işlemleri ile uğraşabilirsiniz. Ancak C, Java veya JavaScript dillerini kullanıyor olmamız bir çeşit performans iyileştirimidir. Çünkü altta yatan platform hakkında uğraşmamıza gerek kalmaz. React’te, varsayılan state performansı zaten hızlı olduğu için, performans iyileştirimi üzerinde düşünmeniz bile gerekmeden uygulama geliştirebilirsiniz.
Ancak konuşmanın bu kısma o kadar odaklanılmamıştır ve gerçek DOM işlemlerinin yavaş olduğu fikri yayılmıştır.
Peki virtual DOM yavaş mı çalışıyor?
Tam olarak değil. Bunun yerine “Virtual DOM olabildiğince hızlı çalışır.” demek daha uygun olur. Tabi bazı kurallara da uymak gerekir.
React, her state değişikliğinde performans konusunda endişe etmeden bütün uygulamanızı render edebileceğinizi vadetmiştir. Pratikte bu durum doğru değildir. Çünkü performans konusunda endişe edilmeseydi, React’in bir bileşeni güncellemesini pas geçmesini sağlayan shouldComponentUpdate
gibi bir fonksiyona gerek kalmazdı.
Uygulamada shouldComponentUpdate
kullanılsa bile, bütün uygulamanın virtual DOM’unun tek seferde güncellenmesi büyük bir çalışma gerektirir. Bu nedenle React ekibi, tek seferde güncelleme yerine parça parça güncellemeye izin veren React Fiber‘i tanıtmıştır. Bu sayede, DOM güncellemeleri main thread’i uzun süreler boyunca bloklamayacaktır. Fakat bu, güncelleme işinin büyüklüğü ve süresine etki etmemektedir.
Peki virtual DOM’un yol açtığı gereksiz yük nereden geliyor?
Açıkçası diffing işlemi bedavaya gerçekleşmez. Diffing yapmadan (önceki snapshot ile yeni oluşturulan virtual DOM’u karşılaştırmadan) gerçek DOM üzerinde değişiklikleri direkt olarak uygulayamazsınız. Öncelikle dilerseniz aşağıdaki HelloMessage örneğini ele alalım:
function HelloMessage(props) {
return (
<div className=“greeting”>
Hello {props.name}
</div>
);
}
name
prop’unu ‘world”den ‘everybody”e değiştirdiğimizi varsayalım. Diff işlemi aşağıdaki şekilde çalışacaktır:
- En üst elemana bakılır. Her iki snapshot’ta da tek bir div elemanı olduğu için aynı DOM düğümünü kullanmaya devam edilir.
- Eski
div
elementinin bütün attribute’leri sırayla ele alınır. Eski bir attribute değiştirilmiş mi, silinmiş mi yoksa eklenmiş mi kontrol edilir. İki durumda da aynı şekilde “greeting” değerine sahip bir className attribute’ü olduğu için devam edilir. div
elementinin içerisine gidilir. Buradaki metnin değiştiği farkedilir ve gerçek DOM güncellenir.
Bu bileşende oluşacak DOM güncellemelerin çoğunda uygulamanın temel yapısı değişmeden kalacağı için, buradaki 3 adımdan yalnızca sonuncusunun bizim için bir değeri var. Yani direkt olarak 3. adıma atlayarak aşağıdaki şekilde bir değişiklik yapabiliriz:
if (changed.name) {
text.data = name;
}
Svelte derleyicisinin ürettiği güncelleme kodu da hemen hemen böyledir. Geleneksel UI framework’lerinin çalışma zamanında bekleyerek diffing yapmasının aksine Svelte, uygulama içerisinde nelerin değişeceğini derleme zamanında bilir.
Sadece yavaşlatan şey diffing işlemi de değildir
React ve diğer virtual DOM framework’leri tarafından kullanılan diffing algoritmaları yeterince hızlı çalışır. Ancak asıl performans yükü bileşenlerin kendi içerisinde oluşur. Örneğin aşağıdaki şekilde bir bileşen oluşturmamanız gerekir:
function StrawManComponent(props) {
const value = expensivelyCalculateValue(props.foo);
return (
<p>the value is {value}</p>
);
}
Çünkü props.foo
‘nun değişip değişmediğine bakmaksızın her güncellemede value
değişkeni yeniden hesaplanacaktır. Ancak bu tarz gereksiz hesaplama ve bellek ayırma işlemlerinin çok daha iyi huylu görünen şekillerde yapılması son derece yaygındır:
function MoreRealisticComponent(props) {
const [selected, setSelected] = useState(null);
return (
<div>
<p>Selected {selected ? selected.name : ‘nothing’}</p>
<ul>
{props.items.map(item =>
<li>
<button onClick={() => setSelected(item)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
Burada, props.items
değerinin değişip değişmediğine bakılmaksızın her state değişikliğinde, her birinin kendi inline event handler’ı bulunan yeni bir <li>
elemanı dizisi oluşturulur. Eğer performansı çok kafaya takmıyorsanız, bu kodu optimize etmeye çalışmazsınız. Çünkü yeterince hızlı çalışır. Fakat daha hızlı çalıştırmanın bir yolu vardır: bu şekilde kodlamamak.
Varsayılan olarak gereksiz yere iş yapan kodu barındırmanın tehlikesi, yapılan iş çok önemsiz olsa bile uygulamanın büyüdükçe daha yavaş bir hale geleceğidir. Bu noktada artık bariz bir şekilde uygulamanın tıkanmasına yol açan bir kısım olmayacağı için, optimize etmek de oldukça zordur. Svelte bu tarz durumlara düşmeyi engellemek için tasarlanmıştır.
React Hooks’un öngörülebilen sonuçları sayesinde oluşan gereksiz işi yarıya indirebilmektedir.
Peki virtual DOM bu kadar sorunluysa neden diğer framework’ler bunu kullanıyor?
Öncelikle virtual DOM’un spesifik bir özellik olmadığını anlamak önemli. Çünkü vDOM, deklaratif ve state bazlı UI geliştiriminin bir sonucu olacak şekilde doğal olarak ortaya çıkan bir sonuçtur. Virtual DOM, state geçişleri hakkında düşünmenize gerek kalmadan yeterli seviyede bir performans ile uygulama geliştirimi için önemlidir. Virtual DOM ile daha az hatalı kod oluşur ve gereksiz kod yükünden kaçınılarak yaratıcılığa daha fazla zaman kalır.
Svelte’te virtual DOM kullanılmadan benzer bir programlama modeli oluşturulduğu için buna gerek duyulmaz.
Svelte reaktiftir ve karmaşık state yönetimi kütüphanelerine ihtiyaç duymaz
Svelte’in 3. versiyonu öncesinde reaktiviteyi sağlamak için, React’te yer alan this.setState()
metodu gibi bir this.set()
metodu bulunuyordu. Hook’ların ortaya çıkışı ile state yönetimi farklı bir modaya evrildi ve birçok framework kendi hook implementasyonunu yapmaya başladı. Fakat Svelte’te hook’ların uyarlanması, içerisine doğal olmayan kodların ekleneceği ve garbage collector için gereksiz iş yükü doğuracağı için gereksizdi. Düşük performanslı gömülü cihazlarda dahi çalışabilen bir framework için hook’un uyarlanması iyi bir durum değildi. State yönetimi için en iyi API, belki de hiç API’nin bulunmamasıydı.
Bu nedenle Svelte 3’te state atamalarının yapılması için aşağıdaki gibi basit bir şekilde sadece atama operatörü kullanıldı:
count += 1;
Svelte bir derleyici olduğu için bu kodu arka planda aşağıdaki gibi derleyebilir:
count += 1; $$invalidate(‘count’, count);
Daha da önemlisi, proxy kullanımı veya çeşitli nokta operatörü erişimcileri kullanmadan Svelte’in state yönetimini gerçekleştirebiliyor olmasıdır. Sadece bir değişken ile state atamaları yapılır.
Sonuç olarak
Svelte, yalın kod yapısı, virtual DOM bulundurmaması ve sayfaya derlenerek eklenmesi ile React ve Vue’den ayrılıyor. Şu anda Svelte projesi Github üzerinde 23 bin yıldız almış durumda. Bu konumda Preact ile benzer yıldız miktarına sahip. Kim bilir belki de gelecekte diğer frameworklerin önüne geçecektir. Svelte hakkında yardım almak için discord kanalına giriş yapabilirsiniz. Bu yazı hakkında öneri ve görüşlerinizi yorum bölümünden bize yazabilirsiniz. Bir sonraki yazıda görüşmek üzere…