Vue 3 Composition API: Gerçek Projede Karşılaştığım Püf Noktalar
Vue 3'ün Composition API'si, frontend geliştirme dünyasında bir değişim oluşturdu. Bu yazıda, gerçek projelerimde karşılaştığım püf noktaları ve çözümleri paylaşmak istiyorum.
1. Reactive Data: ref vs reactive - Hangi Durumda Hangisi?
Options API'den Composition API'ye geçerken en çok kafam karışan konulardan biri buydu. İlk başta her şey için reactive kullanıyordum, sonra her şey için ref'e geçtim. Zamanla optimal kullanım şekillerini öğrendim.
Primitive Değerler İçin ref
// ✅ Doğru kullanım
const count = ref(0)
const isLoading = ref(false)
const username = ref('')
// ❌ Yanlış kullanım
const count = reactive(0) // Primitive değerler reactive olamaz
Kompleks Objeler İçin reactive
// ✅ Form handling için ideal
const form = reactive({
username: '',
email: '',
password: ''
})
// ✅ State management için
const userState = reactive({
data: null,
loading: false,
error: null
})
Blog yazıları listesi için reactive kullandığımda, array metodları (push, filter, map) daha temiz kod yazmamı sağladı.
2. Nested Reactivity ve toRefs Kurtarıcısı
Takipp projesinde envanter yönetimi için derinlemesine nested objeler kullanıyordum. Burada reactivity kaybetme sorunu yaşadım.
// ❌ Sorunlu kod
const productData = reactive({
product: {
details: {
name: '',
category: ''
},
inventory: {
quantity: 0,
location: ''
}
}
})
// Destructuring ile reactivity kaybı
const { name, category } = productData.product.details // Reactive değil!
Çözüm: toRefs kullanımı
// ✅ Doğru yaklaşım
const productData = reactive({
product: {
details: {
name: '',
category: ''
}
}
})
// Reactivity korunur
const { name, category } = toRefs(productData.product.details)
// Template'de direkt kullanabilirsin
// {{ name }} {{ category }}
3. Composable'lar: Kod Tekrarının Sonu
Bir projemde aynı API call pattern'ini birçok yerde kullanıyordum. Composable pattern'i hayat kurtardı.
API Calls İçin Reusable Composable
// composables/useApi.js
export const useApi = (url) => {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const execute = async () => {
loading.value = true
error.value = null
try {
const response = await $fetch(url)
data.value = response
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
return {
data: readonly(data),
loading: readonly(loading),
error: readonly(error),
execute
}
}
Kullanımı
// pages/blog.vue
const { data: posts, loading, execute: fetchPosts } = useApi('/api/posts')
onMounted(() => {
fetchPosts()
})
Püf Nokta: readonly kullanarak state'i dışarıdan değiştirilemez hale getirmek, beklenmedik bug'ları önlüyor.
4. Performance: computed vs watch
Kim Var projesinde blog yazılarını filtreleme işleminde performance sorunu yaşadım. İlk başta watch kullanıyordum, sonra computed'a geçince farkı gördüm.
❌ Yanlış Yaklaşım: watch
// ❌ Her değişiklikte yeniden hesaplama
const searchTerm = ref('')
const filteredPosts = ref([])
watch(searchTerm, (newTerm) => {
filteredPosts.value = posts.value.filter(post =>
post.title.toLowerCase().includes(newTerm.toLowerCase())
)
})
✅ Doğru Yaklaşım: computed
// ✅ Cache'lenmiş hesaplama
const searchTerm = ref('')
const filteredPosts = computed(() => {
if (!searchTerm.value) return posts.value
return posts.value.filter(post =>
post.title.toLowerCase().includes(searchTerm.value.toLowerCase())
)
})
Fark: computed sadece bağımlılıkları değiştiğinde hesaplanır ve sonucu cache'ler. Aynı değer için tekrar hesaplama yapmaz.
5. TypeScript ile Composition API
Takipp projesinde TypeScript kullanırken, type inference konusunda bazı püf noktalar öğrendim.
Generic Composable'lar
// composables/useLocalStorage.ts
export const useLocalStorage = <T>(
key: string,
defaultValue: T
) => {
const storedValue = ref<T>(defaultValue)
const setValue = (value: T) => {
storedValue.value = value
localStorage.setItem(key, JSON.stringify(value))
}
return {
value: storedValue,
setValue
}
}
// Kullanım - tip otomatik çıkarım
const { value: userPrefs, setValue } = useLocalStorage('userPrefs', {
theme: 'dark',
language: 'tr'
})
Props ile Type Safety
// components/ProductCard.vue
interface Props {
product: {
id: number
name: string
price: number
}
showDetails?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showDetails: true
})
// Artık props tam type-safe
console.log(props.product.name) // string type guaranteed
6. Lifecycle Hooks: Yeni Yaklaşım
Options API'deki lifecycle hook'ları Composition API'de farklı kullanıyoruz:
// setup() içinde
onMounted(() => {
console.log('Component mounted!')
})
onBeforeUnmount(() => {
// Cleanup işlemleri
clearInterval(intervalId)
})
// Multiple hooks
onMounted(() => {
initializeChart()
})
onMounted(() => {
fetchData()
})
Püf Nokta: Aynı lifecycle hook'u birden fazla kez kullanabilirsin. Hepsi çalışır!
7. Gerçek Dünya Örneği: Takipp Dashboard
Takipp projesinde dashboard component'inde öğrendiğim pattern:
// composables/useDashboard.js
export const useDashboard = () => {
// State
const dashboardData = reactive({
products: [],
analytics: null,
loading: false
})
// Computed
const totalProducts = computed(() => dashboardData.products.length)
const criticalStock = computed(() =>
dashboardData.products.filter(p => p.quantity < 10)
)
// Methods
const refreshData = async () => {
dashboardData.loading = true
try {
const [products, analytics] = await Promise.all([
$fetch('/api/products'),
$fetch('/api/analytics')
])
dashboardData.products = products
dashboardData.analytics = analytics
} finally {
dashboardData.loading = false
}
}
return {
...toRefs(dashboardData),
totalProducts,
criticalStock,
refreshData
}
}
Sonuç
Composition API, kod organizasyonu ve tekrar kullanılabilirlik açısından muazzam avantajlar sağlıyor. Ancak doğru pattern'leri öğrenmek zaman alıyor. Bu yazıda paylaştığım püf noktalar, gerçek projelerimde saatlerce debug'dan beni kurtaran deneyimler.
Composition API'yi öğrenirken sabırlı olun, Options API'den tamamen koparmaya çalışmayın. İkisini de bilmek, mevcut projeleri migrate ederken çok işinize yarayacak.
Bir sonraki yazımda Nuxt 3'te performance optimizasyonu konusunu ele alacağım. Gerçek projelerimde uyguladığım teknikleri paylaşacağım.
Bu yazı projelerdeki deneyimlerimden çıkardığım notları içeriyor. Yazıda hata farkederseniz veya sorularınız varsa benimle iletişime geçebilirsiniz.