Scoped Slot Nedir?
Vue.js'de bileşenler arası iletişim ve veri akışı, props aşağı, event'lar yukarı prensibi ile şekillenir. Ancak, scoped slot'lar bu paradigmayı esneterek, alt bileşenin (child component) verilerini üst bileşenin (parent component) kapsamında (scope) kullanılabilir hale getiren güçlü bir mekanizma sunar. Temel olarak, bir üst bileşenin alt bileşene gönderdiği bir şablon parçasının, alt bileşenden gelen verilere erişmesini sağlarlar.
Bu, render props desenine benzer bir işlevsellik kazandırır ve bileşenleri son derece yeniden kullanılabilir ve esnek hale getirir. Scoped slot'lar olmadan, bir liste bileşeninin her öğesini nasıl render edeceğini önceden bilmesi gerekirdi. Scoped slot ile, verinin kendisi alt bileşenden gelirken, bu verinin nasıl görüntüleneceği üst bileşenin kontrolünde ve sorumluluğunda kalır.
- Veri sahibi (alt bileşen) ile görsel sunum (üst bileşen) arasında net bir ayrım sağlar.
- Bileşen API'sini, görsel detaylardan bağımsız, saf veri odaklı hale getirir.
- Kapsülleme (encapsulation) ilkesini bozmadan, özelleştirilebilirliği en üst düzeye çıkarır.
Bu konsept, özellikle generic (jenerik) bileşenler oluştururken kritik öneme sahiptir. Bir tablo, liste, select dropdown veya veriye bağlı herhangi bir bileşen, scoped slot'lar sayesinde tüketildiği bağlamın gerektirdiği her türlü özel render işlemini destekleyebilir. Böylece, bileşenin temel mantığı (veri çekme, sıralama, filtreleme) bir kez yazılır, ancak görünümü prjenin ihtiyaçlarına göre sonsuz şekilde özelleştirilebilir.
Temel Kullanım ve Yazımı
Scoped slot'ın temel yapısı, alt bileşende bir `
// Alt Bileşen (ChildComponent.vue)
<template>
<div>
<slot :user="currentUser" :role="userRole"></slot>
</div>
</template>
<script>
export default {
data() {
return {
currentUser: { name: 'Ahmet', id: 123 },
userRole: 'Admin'
};
}
};
</script>
Üst bileşen, bu scoped slot'u kullanmak için iki ana sözdizimi kullanabilir: Vue 2.x'te yaygın olan `slot-scope` attribute'u ve Vue 3'ün yanı sıra Vue 2.6+'da desteklenen `v-slot` direktifi. `v-slot`, daha modern ve önerilen yöntemdir. Kullanımı aşağıdaki gibidir. Burada, alt bileşenden gelen tüm slot prop'ları, `slotProps` adlı bir değişkende toplanır. Bu isim gelenekseldir, ancak herhangi bir geçerli değişken adı kullanılabilir.
Üst bileşenin şablonunda, alt bileşen etiketleri içine bir `<template>` yapısı yerleştirilir ve bu template'e `v-slot` direktifi eklenir. Bu direktifin değeri, alt bileşenden gelen prop'lara erişmek için kullanılacak değişkenin adını belirler. Bu değişken, `<template>` içindeki tüm alanda kullanılabilir.
| Sözdizimi | Vue Versiyonu | Açıklama |
|---|---|---|
slot-scope="slotProps" |
2.5+ (Eski Yöntem) | Desteklenmeye devam eder, ancak yeni projelerde v-slot kullanılması önerilir. |
v-slot="slotProps" |
2.6+ ve 3+ | Modern ve tercih edilen sözdizimi. Daha açıklayıcı ve yapısal olarak daha temizdir. |
v-slot:default="slotProps" |
2.6+ ve 3+ | Varsayılan (default) slot için açıkça belirtilmiş kullanım. Yapısal olarak en doğru olandır. |
Aşağıdaki örnek, üst bileşenin scoped slot'u nasıl tükettiğini gösterir. Alt bileşenden gelen `user` ve `role` değerleri, `slotProps` nesnesi içinden alınır ve üst bileşenin şablonunda doğrudan kullanılır. Bu, üst bileşene, bu verileri istediği gibi biçimlendirme ve yerleştirme özgürlüğü tanır.
// Üst Bileşen (ParentComponent.vue) - v-slot ile
<template>
<div>
<child-component>
<template v-slot="slotProps">
<p>Kullanıcı Adı: {{ slotProps.user.name }}</p>
<p>Rolü: <strong>{{ slotProps.role }}</strong></p>
</template>
</child-component>
</div>
</template>
Bu yapı, alt bileşenin içsel durumunu (currentUser, userRole) dışarıya, üst bileşenin görünüm katmanına güvenli bir şekilde "açmasını" sağlar. Alt bileşen verinin ne olduğunu bilir, üst bileşen ise bu verinin nasıl görüneceğini kontrol eder. Bu iş bölümü, bileşen tabanlı mimaride sürdürülebilirliği ve esnekliği artıran en önemli faktörlerden biridir.
Slot Scope'tan v-slot'a Geçiş
Vue.js 2.6 sürümü, slot API'sinde büyük bir iyileştirme olan `v-slot` direktifini getirdi. Bu yeni sözdizimi, `slot-scope` attribute'una kıyasla daha tutarlı, okunabilir ve yapısal olarak daha sağlam bir kullanım sunar. `slot-scope`, doğrudan bir HTML elementine yazılabiliyordu ve bu, özellikle adlandırılmış slotlarla (named slots) birlikte kullanıldığında kafa karışıklığına yol açabiliyordu.
`v-slot` direktifi ise her zaman bir `<template>` etiketi üzerinde kullanılır. Bu, hangi içeriğin hangi slota ait olduğunu görsel olarak net bir şekilde ayırt etmeyi kolaylaştırır. Ayrıca, adlandırılmış slotlarla birlikte kullanımı çok daha açıktır. `v-slot:header="props"` şeklindeki bir yazım, içeriğin "header" adlı slota gideceğini ve o slotun prop'larını alacağını anında gösterir. Bu tutarlılık, kodun bakımını ve anlaşılırlığını önemli ölçüde artırır.
Bir diğer önemli avantaj, yapısal atama (destructuring) desteğidir. `v-slot` ile, alt bileşenden gelen slot prop'larını doğrudan şablonda yapısal olarak parçalayabilirsiniz. Bu, `slotProps.user.name` yerine doğrudan `{ user }` şeklinde erişim sğlayarak şablon kodunu daha temiz hale getirir. Bu özellik, modern JavaScript geliştiricileri için çok daha tanıdık ve verimli bir model sunar ve şablon içindeki tekrarları azaltır.
// slot-scope ile (Eski Yöntem)
<child-component>
<div slot="default" slot-scope="{ user, role }">
{{ user.name }} - {{ role }}
</div>
</child-component>
// v-slot ile (Modern ve Önerilen Yöntem)
<child-component>
<template v-slot:default="{ user, role }">
<div>
{{ user.name }} - {{ role }}
</div>
</template>
</child-component>
// Kısaltılmış #default Sözdizimi (Vue 2.6+)
<child-component>
<template #default="{ user, role }">
<div>
<strong>{{ user.name }}</strong> <em>({{ role }})</em>
</div>
</template>
</child-component>
İleri Düzey Kullanım Senaryoları
Scoped slot'ların gerçek gücü, temel örneklerin ötesine geçildiğinde ortaya çıkar. İleri düzey senaryolarda, bu mekanizma, uygulama mimarisinde bileşenler arasında esnek ve güçlü bir sözleşme (contract) kurulmasını sağlar. En yaygın kullanım alanı, veri listesi veya koleksiyon işleyen soyut bileşenlerdir. Bu bileşenler, veriyi almak, sayfalamak, sıralamak ve filtrelemek gibi iş mantığını (business logic) yönetirken, her bir veri öğesinin (item) nasıl render edileceği tamamen tüketici bileşene bırakılır.
Bir diğer karmaşık senaryo, "renderless component" (renderless bileşen) olarak adlandırılan yapılardır. Bu bileşenler, kendileri hiçbir HTML elementi render etmezler; yalnızca mantık (state, metodlar) sağlarlar. Tüm render sorumluluğunu, scoped slot aracılığıyla aldığı bu mantığı kullanan üst bileşene devrederler. Örneğin, fare konumunu takip eden, bir API'den veri çeken veya form doğrulama mantığını barındıran bir renderless bileşen, bu işlevselliği bir scoped slot prop'u olarak sunar.
Bu yaklaşım, mantığı ve görünümü ayırma ilkesini en üst seviyeye taşır. Aynı veri yönetim mantığı, farklı UI/UX ihtiyaçları olan birden fazla projede veya aynı proje içinde farklı bağlamlarda tamamen yeniden kullanılabilir. Bu, kod tekrarını (code duplication) önlemenin ve tutarlı bir iş mantığı katmanı oluştrmanın en etkili yollarından biridir. Renderless bileşenler, scoped slot'lar olmadan uygulanması neredeyse imkansız olan bu soyutlamayı mümkün kılar.
| Senaryo Türü | Alt Bileşenin Rolü | Scoped Slot'un Sağladığı Avantaj |
|---|---|---|
| Generic Liste/Tablo | Veriyi hazırlar (sayfala, sırala, filtrele). | Her satırın/sütunun render şeklini üst bileşen belirler. UI framework'ünden bağımsızdır. |
| Renderless Bileşen | Saf JavaScript mantığı sağlar (veri, metodlar). | Mantık tamamen kapsüllenir; görünüm %100 özelleştirilebilir. Yüksek seviyede yeniden kullanılabilirlik. |
| Karmaşık Form Yapıları | Form state'ini ve doğrulama kurallarını yönetir. | Her form alanının (input, select) özel layout ve stilini üst bileşen kontrol edebilir. |
| Yüksek Düzeyde Özelleştirilebilir UI Kit Bileşenleri | Temel davranışı ve erişilebilirliği sağlar. | Geliştiricinin, bileşenin her bir iç detayını (icon, metin, boşluklar) override etmesine izin verir. |
- Esneklik (Flexibility): Tek bir bileşen, onlarca farklı görünümü ve davranışı, ek props veya config olmadan destekleyebilir.
- Sorumluluk Ayrımı (Separation of Concerns): Alt bileşen veri ve iş mantığından, üst bileşen kullanıcı arayüzünden sorumlu olur. Bu, hata ayıklamayı ve test etmeyi kolaylaştırır.
- Geleceğe Uyumluluk (Future-proofing): Alt bileşenin iç mantığı değişse bile (örneğin, bir harici kütüphaneden başka birine geçiş), slot prop'ları aynı arabirimi (interface) sağladığı sürece üst bileşenler bozulmaz.
Bu tekniklerin etkin kullanımı, özellikle büyük ölçekli uygulamalarda ve tasarım sistemi (design system) bileşen kütüphaneleri geliştirirken kritik öneme sahiptir. Scoped slot'lar, bileşen tabanlı mimaride "açık-kapalı prensibini" (open-closed principle) uygulamanın pratik bir yoludur: Bileşenler genişletilmeye açık (yeni render şekilleri için), ancak değiştirilmeye kapalı (temel mantık değişmeden kalır) olurlar.
Sonuç olarak, scoped slot'lar Vue.js ekosisteminde kompozisyon (composition)'un en güçlü araçlarından biridir. Sadece veri geçirmekten öte, render sorumluluğunun dinamik olarak devredilmesini sağlayarak, geliştiricilere standart props/events modelinin ötesinde bir kontrol ve özelleştirme gücü verir. Bu modelde bileşenler, katı bir "kara kutu" olmak yerine, davranışlarını dış dünyaya açan ve işbirliğine dayalı bir yapı haline gelir.
Gerçek Dünya Örneği: Veri Tablosu Bileşeni
Scoped slot konseptini somutlaştırmak için, en yaygın kullanım alanlarından biri olan generic bir veri tablosu bileşeni oluşturalım. Bu `DataTable` bileşeninin amacı, bir dizi veriyi almak, sıralama ve sayfalama gibi temel özellikleri uygulamak, ancak her bir sütunun ve satırın tam olarak nasıl görüntüleneceğini tüketici bileşene bırakmaktır. Bu yaklaşım, bileşeni herhangi bir UI kütüphanesine (Bootstrap, Vuetify, Tailwind) veya özel tasarıma uyarlanabilir kılar.
Bileşenimiz, `items` adında bir prop aracılığıyla bir dizi veri nesnesi alacak ve `columns` prop'u ile sütun yapılandırmasını kabul edecek. Asıl sihir ise, her bir sütunun içeriğini render etmek için bir scoped slot sağlamasında yatar. Bu slot, o sütundaki mevcut satırın verisine (`row`) ve sütun yapılandırmasına (`column`) erişim sağlayacak. Ayrıca, varsayılan bir scoped slot da sağlayarak, geliştiricinin tüm satırı tek bir slot içinde özelleştirmesine de olanak tanıyabiliriz.
// DataTable.vue (Alt Bileşen - Kısaltılmış)
<template>
<table class="table">
<thead>
<tr>
<th v-for="col in columns" :key="col.key" @click="sortBy(col.key)">
{{ col.title }}
<span v-if="sortKey === col.key">{{ sortOrder > 0 ? '▲' : '▼' }}</span>
</th>
</tr>
</thead>
<tbody>
<!-- Varsayılan Satır Slotu -->
<template v-if="$scopedSlots.default">
<tr v-for="(item, index) in sortedItems" :key="index">
<slot :row="item" :index="index"></slot>
</tr>
</template>
<!-- Sütun Bazlı Slotlar -->
<template v-else>
<tr v-for="(item, index) in sortedItems" :key="index">
<td v-for="col in columns" :key="col.key">
<slot :name="col.key" :row="item" :column="col">
<!-- Varsayılan Görüntüleme -->
{{ item[col.key] }}
</slot>
</td>
</tr>
</template>
</tbody>
</table>
</template>
<script>
export default {
props: ['items', 'columns'],
data() { return { sortKey: '', sortOrder: 1 }; },
computed: {
sortedItems() { /* Sıralama mantığı burada */ }
},
methods: {
sortBy(key) { /* Sıralama tetikleme */ }
}
};
</script>
Bu yapıyı kullanan bir üst bileşen, artık `DataTable`'ın sunduğu sıralama gibi özelliklerden faydalanırken, her bir hücrenin içeriğini tamamen özgürce şekillendirebilir. Örneğin, bir "durum" sütununda metin yerine bir badge (etiket) göstermek, bir "aksiyon" sütununa butonlar eklemek veya belirli değerlere koşullu stil uygulamak mümkün hale gelir. Bu, üst bileşene görsel sunum konusunda sınırsız bir kontrol alanı açar ve `DataTable` bileşenini her projede tekrar tekrar kullanılabilir kılar.
Aşağıdaki örnek, üst bileşenin bu generic tabloyu nasıl kullanabileceğini gösterir. `#default` slotu ile tüm satırın render sorumluluğu alınabilir veya daha yaygın olarak, her sütun için ayrı adlandırılmış scoped slotlar (`#status`, `#actions`) kullanılabilir. Bu adlandırılmış slotlar, `columns` prop'unda tanımlanan `key` değerleri ile eşleşir. Bu pattern, tablo bileşenlerinde en yaygın ve güçlü kullanım şeklidir.
// Üst Bileşen Kullanımı
<DataTable :items="users" :columns="columns">
<!-- 'status' sütunu için özel slot -->
<template #status="{ row }">
<span :class="`badge bg-${row.status === 'Aktif' ? 'success' : 'secondary'}`">
{{ row.status }}
</span>
</template>
<!-- 'actions' sütunu için özel slot -->
<template #actions="{ row }">
<button class="btn btn-sm btn-primary" @click="editUser(row.id)">Düzenle</button>
<button class="btn btn-sm btn-danger ms-1" @click="deleteUser(row.id)">Sil</button>
</template>
<!-- 'email' sütunu boş bırakıldı, varsayılan görüntüleme (item[col.key]) kullanılacak -->
</DataTable>
<script>
export default {
data() {
return {
users: [ /*...*/ ],
columns: [
{ key: 'name', title: 'Ad Soyad' },
{ key: 'email', title: 'E-posta' },
{ key: 'status', title: 'Durum' }, // #status slotu tarafından render edilecek
{ key: 'actions', title: 'İşlemler' } // #actions slotu tarafından render edilecek
]
};
}
};
</script>
Bu örnek, scoped slot'ların bileşen yeniden kullanılabilirliğini (reusability) ve özelleştirilebilirliğini (customizability) nasıl en üst seviyeye taşıdığını net bir şekilde gösterir. `DataTable` bileşeni, karmaşık bir tablo mantığını kapsüllerken, kullanıcı arayüzü detaylarından tamamen soyutlanır. Bu sayede, aynı `DataTable` bileşeni, farklı sayfalarda birbirinden tamamen farklı görünümlere bürünebilir. Bu pattern, Vue.js ile geliştirilen tüm büyük ölçekli UI kütüphanelerinin (Vuetify, Element Plus, Quasar) temel taşlarından biridir.
Scoped slot'ları ustalıkla kullanmak, bir Vue.js geliştiricisinin kütüphane ve araçları daha derinlemesine anlamasını ve kendi bileşen kütüphanelerini profesyonel seviyede tasarlayabilmesini sağlar. Bu teknik, bileşenler arasındaki sıkı bağlılığı (tight coupling) azaltır, test edilebilirliği artırır ve uygulamanın uzun vadeli sürdürülebilirliğine önemli bir katkıda bulunur. Vue.js ekosisteminde, kompozisyon API'si gibi diğer gelişmiş özelliklerle birlikte kullanıldığında, scoped slot'lar modern frontend geliştirmenin vazgeçilmez araçlarından biri olmaya devam etmektedir.