Ana içeriğe geç

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.