Konu Başlıkları
Yükleniyor...

Web Bileşenleri ve Çerçeve Bileşenleri: Sınır Nerede Çiziliyor

Web Components ile Çerçeve Bileşenleri Arasındaki Gerçek Fark

Front-end tarafında "bileşen" kelimesi iki ayrı şeyi anlatıyor: tarayıcının kendi API'leriyle tanımlanan Web Bileşenleri, bir de bir çerçeveye ait olan React, Vue, Angular bileşenleri. İkisi aynı işi yapmıyor, aynı yerde de durmuyor. Aradaki fark genelde sözdiziminde aranır; oysa asıl ayrım, verinin bileşene nasıl geçtiğinde.

Web Bileşeni üç parçadan ibaret

Custom Elements yeni bir HTML etiketi tanımlamanı sağlar. Shadow DOM o etiketin içine ayrı bir DOM ağacı asar, böylece dışarıdaki seçiciler içeriye ulaşamaz. <template> ise sayfa açılışında işlenmeyen, JavaScript çağırınca klonlanan bir biçimlendirme parçası tutar.

Üçü de tek bir sınıfa sığar:

class RenkSecici extends HTMLElement { connectedCallback() { this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = '<style>p { margin: 0 }</style><p>Seçili renk</p>'; } } customElements.define('renk-secici', RenkSecici);

Dışarıdan bakınca <renk-secici> artık <input> kadar sıradan bir etiket. Tarayıcı tanıyor, normal DOM API'leri üzerinde çalışıyor, derleme adımı istemiyor.

"Çerçeve-bağımsız" iddiası nerede çatlıyor

Web Bileşenleri her çerçevenin içine girer. Girdikten sonra veriyi nasıl alacağı ayrı bir mesele ve sürtünme tam orada başlıyor.

HTML nitelikleri metindir. Bileşene dizi, nesne ya da fonksiyon geçirmek istediğin anda nitelik yolu tıkanır:

// Nitelik değeri metne çevrilir el.setAttribute('secenekler', ['kirmizi', 'mavi']); el.getAttribute('secenekler'); // 'kirmizi,mavi' // Bileşenin gerçek arayüzü burada el.secenekler = ['kirmizi', 'mavi'];

Yani bir Web Bileşeninin sözleşmesi niteliklerde değil, JavaScript özelliklerinde yazılı. Bunu bilen çerçeve, şablonda yazdığın değerin nitelik mi özellik mi olacağına kendisi karar verir. Vue bunu yıllardır yapıyor, Angular [prop] ve (event) sözdizimiyle zaten ikisini ayırıyor. React 19'a kadar yapmıyordu: React DOM her değeri niteliğe çeviriyor, diziyi virgüllü metne, nesneyi [object Object] dizisine düşürüyordu.

Olay tarafında da aynı hikâye. Bileşenin dispatchEvent ile yaydığı özel olay React'in sentetik olay sisteminden geçmez; onSecim diye bir prop yazamazsın, ref alıp addEventListener bağlarsın. React 19 her ikisini de düzeltti. Ama çerçeve sürümüne bağlı bir düzeltme, "çerçeve-bağımsız" tanımını olduğu gibi zayıflatıyor.

Üstüne küçük ayarlar biniyor: Angular'da CUSTOM_ELEMENTS_SCHEMA, Vue'da compilerOptions.isCustomElement. Beş dakikalık işler, ama "hiçbir değişiklik gerekmeden çalışır" cümlesini yanlışlıyorlar.

Kapsülleme duvar değil, filtre

Shadow DOM stilleri izole eder, bu doğru. İzolasyonun tek yönlü ve seçici olduğu kısmı atlanıyor. Kalıtsal CSS özellikleri sınırı rahatça geçer: color, font-family, line-height dışarıdan içeri akar. CSS özel özellikleri de kalıtımla akar, bileşene tema vermenin standart yolu zaten bu:

renk-secici { --secici-arkaplan: #f4f4f5; }

Bir de ::part() var. Bileşeni yazan kişi içerideki bir düğüme part="dugme" koymuşsa, dışarıdan renk-secici::part(dugme) ile ona stil verirsin. Sık tekrarlanan "harici CSS Shadow DOM'u geçemez" cümlesi bu yüzden yanlış. Geçer; yalnızca bileşen yazarının açık bıraktığı kapılardan geçer, gerisi kapalıdır. Aradaki fark önemsiz değil: biri bileşeni dokunulmaz yapar, diğeri onu sözleşmeli hale getirir.

Çerçeve tarafındaki kapsamlı CSS ise baştan daha gevşek. Vue'nun scoped stili ya da CSS Modules, sınıf adlarına benzersiz bir ek takar. Bu, senin stilinin dışarı sızmasını engeller. Dışarıdan gelen bir .btn { padding: 0 } kuralını engellemez.

Polyfill tartışması bitti, sunucu tarafı bitmedi

Custom Elements v1 ve Shadow DOM v1 bütün güncel tarayıcılarda yıllardır yerleşik. Polyfill yükünü hâlâ dezavantaj diye listeleyen karşılaştırma tabloları 2018'in tablolarıdır, kopyalana kopyalana bugüne kadar gelmişler. Aynı metinlerin bir alt başlıkta "tarayıcı desteği sürekli iyileşiyor" yazıp bir üst başlıkta polyfill maliyetinden şikâyet etmesi de bu kopyalamanın izi.

Kapanmamış olan yer sunucu tarafı. Bir custom element'in içeriğini JavaScript üretir, dolayısıyla sunucudan gelen HTML'de o içerik yoktur; JavaScript inene kadar kullanıcı boş bir etiketle oturur. Declarative Shadow DOM bunun için tanımlandı ve tarayıcı desteği tamamlandı:

<renk-secici> <template shadowrootmode="open"> <p>Seçili renk</p> </template> </renk-secici>

Standart hazır, araç zinciri değil. Lit'in sunucu tarafı paketi bu yolu kullanıyor, ama kurulumu Next ya da Nuxt'ta SSR açmak kadar hazır gelmiyor. İlk boyanın kritik olduğu bir sayfada bunu göze alacaksan ölçerek karar ver.

Hangisini ne zaman yazarsın

Tasarım sistemini iki ya da daha fazla çerçeveye birden taşıyacaksan Web Bileşeni yaz. Aynı düğmeyi iki ayrı kod tabanında bakımda tutmak, her çerçeve için ince birer sarmalayıcı yazmaktan pahalıdır; fark ilk ciddi tasarım güncellemesinde ortaya çıkar.

Uygulamanın kendisini Web Bileşenleriyle kurma. Durum yönetimi, yönlendirme, liste farkı alma, form doğrulama; bunların hepsini elle yazarsın ve sonunda elinde kendi çerçeven olur. Dokümantasyonu, testi ve topluluğu olmayan bir çerçeve.

Uzun ömür argümanı da göründüğü kadar sağlam değil. Tarayıcı API'si kalıcıdır, orası doğru. Ama on yıl önce yazılmış bir Web Bileşeni bugün kendi build zincirine, kendi polyfill'ine, kendi yardımcı kütüphanesine bağımlı çıkıyor. Kalıcı olan standart; kodun değil.

Kısacası yaprak seviyesindeki arayüz parçaları için Web Bileşeni, uygulamanın iskeleti için çerçeve bileşeni. İkisini aynı kutuya koymaya çalışan her karşılaştırma, yanlış soruyu soruyor.