Vue 3''ün Composition API''si, Options API''nin getirdiği büyük component''lerde mantığın dağılması sorununu çözer. React hooks''a benzer bir paradigma — fonksiyonel, composable, TypeScript dostu. Bu yazı Composition API''yi sıfırdan anlatır ve gerçek dünya örnekleriyle kullanımını gösterir.

<script setup> — Modern Yazım

İlgili rehberler: Yazılım geliştirme süreçleri · PostgreSQL optimizasyonu · Git ileri seviye komutlar · Redis nedir, nasıl kullanılır · Docker ile deploy

<script setup>
import { ref, computed, watch, onMounted } from 'vue';

const count = ref(0);
const doubled = computed(() => count.value * 2);

function increment() {
    count.value++;
}

watch(count, (newVal, oldVal) => {
    console.log(`${oldVal} → ${newVal}`);
});

onMounted(() => {
    console.log('Component mounted');
});
</script>

<template>
    <button @click="increment">{{ count }} × 2 = {{ doubled }}</button>
</template>

ref vs reactive

Vue''de reactive state için iki API var:

  • ref() — primitive değer (number, string, boolean). .value ile erişirsin
  • reactive() — object/array için. Direkt erişim ama destructure edince reactivity kaybolur
import { ref, reactive, toRefs } from 'vue';

// ref — primitive
const count = ref(0);
count.value++;  // JS'te .value gerekir
// template'te {{ count }} — otomatik unwrap

// reactive — object
const state = reactive({
    name: 'Ali',
    age: 30
});
state.age++;  // direkt erişim

// YANLIŞ — destructure reactivity'yi bozar
const { age } = state;  // age artık reactive değil

// DOĞRU — toRefs ile
const { age: ageRef } = toRefs(state);
ageRef.value++;  // çalışır
Bilgi
Best practice: primitive için ref, karmaşık object için reactive. Ama genelde her şeyde ref + .value tutarlılık için önerilir.

Computed

const firstName = ref('Ali');
const lastName = ref('Yılmaz');

// Readonly computed
const fullName = computed(() => `${firstName.value} ${lastName.value}`);

// Writable computed (getter + setter)
const fullNameRW = computed({
    get: () => `${firstName.value} ${lastName.value}`,
    set: (v) => {
        [firstName.value, lastName.value] = v.split(' ');
    }
});

Watch ve WatchEffect

import { ref, watch, watchEffect } from 'vue';

const userId = ref(1);
const user = ref(null);

// watch — explicit source, değişince çalışır
watch(userId, async (newId) => {
    user.value = await fetchUser(newId);
}, { immediate: true });  // ilk yüklemede de çalış

// Birden fazla source
watch([userId, other], ([newId, newOther]) => {});

// Deep watch
watch(stateObject, fn, { deep: true });

// watchEffect — kullandığı tüm reactive'leri otomatik takip eder
watchEffect(() => {
    console.log(`user ${userId.value} = ${user.value?.name}`);
});

Lifecycle Hooks

import {
    onBeforeMount, onMounted,
    onBeforeUpdate, onUpdated,
    onBeforeUnmount, onUnmounted,
    onErrorCaptured
} from 'vue';

onMounted(() => console.log('mounted'));
onUnmounted(() => console.log('cleanup'));

Composables — Yeniden Kullanılabilir Mantık

Composable, Composition API kullanarak yazılmış reusable fonksiyon. React''taki custom hook''ların karşılığı. Vue convention: use prefix''i.

// composables/useFetch.js
import { ref, watchEffect, toValue } from 'vue';

export function useFetch(url) {
    const data = ref(null);
    const error = ref(null);
    const loading = ref(false);

    watchEffect(async () => {
        const u = toValue(url);  // ref veya getter'ı çöz
        data.value = null;
        error.value = null;
        loading.value = true;
        try {
            const res = await fetch(u);
            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            data.value = await res.json();
        } catch (e) {
            error.value = e;
        } finally {
            loading.value = false;
        }
    });

    return { data, error, loading };
}
<script setup>
import { ref } from 'vue';
import { useFetch } from './composables/useFetch';

const userId = ref(1);
const url = computed(() => `/api/users/${userId.value}`);
const { data: user, loading, error } = useFetch(url);
</script>

<template>
    <p v-if="loading">Yükleniyor...</p>
    <p v-else-if="error">{{ error.message }}</p>
    <pre v-else>{{ user }}</pre>
</template>

Props ve Emits

<script setup>
const props = defineProps({
    id: { type: Number, required: true },
    label: { type: String, default: '' }
});

const emit = defineEmits(['update', 'delete']);

function save() {
    emit('update', { id: props.id, changed: true });
}
</script>

<!-- Parent -->
<UserCard :id="1" label="Ali" @update="handleUpdate" />

v-model with Composition

<!-- Parent -->
<CustomInput v-model="text" />

<!-- CustomInput.vue (Vue 3.4+) -->
<script setup>
const model = defineModel();  // iki yönlü binding
</script>

<template>
    <input v-model="model" />
</template>

Pinia (Store)

// stores/user.js
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';

export const useUserStore = defineStore('user', () => {
    const user = ref(null);
    const isAdmin = computed(() => user.value?.role === 'admin');

    async function login(email, password) {
        user.value = await api.login(email, password);
    }

    function logout() {
        user.value = null;
    }

    return { user, isAdmin, login, logout };
});

// Component'te
import { useUserStore } from '@/stores/user';
const userStore = useUserStore();
userStore.login(email, password);

Options API vs Composition API

  • Options API: Yeni başlayanlar için daha kolay, component yapısı zorla düzenli
  • Composition API: Mantık grup''lanabilir, composable ile paylaşılabilir, TypeScript daha iyi
  • Ekip kararı: İkisini de karıştırmayın — bir component ya Options ya Composition

Modern Yazılım Geliştirme ve DevOps Pratikleri

Profesyonel yazılım geliştirme süreci üç pillar üzerine kuruludur: kaynak kontrolü (Git + GitHub/GitLab pull request akışı, code review zorunlu), CI/CD pipeline (otomatik test + lint + build + deploy), ve gözlemlenebilirlik (Sentry/Datadog/Grafana ile log, metric, trace toplama). Test piramidi (unit > integration > e2e) ile kod kalitesini garantilemek, mikroservis mimarisinde Docker container ve Kubernetes orkestrasyonu kullanmak, REST veya GraphQL API tasarımında OpenAPI/GraphQL Schema sözleşmesi tutmak modern standardlardır. Yazılım geliştirme yaşam döngüsü boyunca (gereksinim → tasarım → implementasyon → test → deploy → bakım) Agile/Scrum sprintleri 1-2 hafta, DevOps takımları sürekli teslim (continuous delivery) prensibiyle çalışır.

Vue 3 geliştirme

Vue 3 / Nuxt uygulamaları, Composition API ve Pinia entegrasyonu için iletişime geçin

WhatsApp