Vue 3 Composition API: Reaktivite ve Kompozisyon Rehberi
Vue 3 Composition API konusunda kapsamli rehber. ref, reactive, computed, watch ve composables kullanarak performansli Vue uygulamalari gelistirmek icin gereken her sey.

Vue 3'teki Composition API, bilesenlerin yapilandirilma biciminde kokenlu bir degisimi temsil eder. Kod, secenekler yerine islevsellik bazinda organize edilir; bu da mantik paylasimini ve karmasik uygulamalarin bakimini kolaylastirir.
Bu rehber, Vue.js hakkinda temel bilgi sahibi olunmasini varsayar. Orneklerde Vue 3.2 ile tanitilan ve artik onerilen yaklasim olan <script setup> sozdizimi kullanilmaktadir.
ref ve reactive ile Reaktiviteyi Anlamak
Reaktivite, Vue 3'un temelini olusturur. Reaktif veri olusturmak icin iki ana ilkel mevcuttur: ilkel degerler icin ref ve karmasik nesneler icin reactive.
ref fonksiyonu, bir degeri saran reaktif bir referans olusturur. Bu degere script icerisinde erismek veya degistirmek icin .value ozelligi kullanilmalidir. Sablonda ise Vue bunu otomatik olarak acar.
<script setup>
import { ref } from 'vue'
// Create a ref with initial value 0
const count = ref(0)
// Function that increments the counter
// Note: .value is required in the script
const increment = () => {
count.value++
}
// Reset function
const reset = () => {
count.value = 0
}
</script>
<template>
<!-- In the template, no .value needed -->
<div class="counter">
<p>Counter: {{ count }}</p>
<button @click="increment">+1</button>
<button @click="reset">Reset</button>
</div>
</template>Birbiriyle iliskili birden fazla ozelligi olan nesneler icin reactive, .value gerektirmeden daha dogal bir sozdizimi sunar.
<script setup>
import { reactive } from 'vue'
// reactive for complex objects
// All properties are automatically reactive
const user = reactive({
name: 'Jane Smith',
email: 'jane@example.com',
preferences: {
theme: 'dark',
notifications: true
}
})
// Direct property modification (no .value)
const updateTheme = (newTheme) => {
user.preferences.theme = newTheme
}
// Warning: do not reassign the entire object
// user = { ... } would break reactivity
</script>
<template>
<div>
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
<p>Theme: {{ user.preferences.theme }}</p>
</div>
</template>Genel kural su sekildedir: ilkel degerler (string, number, boolean) icin ref, yapisal nesneler icin reactive kullanilir.
computed ile Hesaplanmis Ozellikler
Hesaplanmis ozellikler (computed), reaktif durumdan degerler turetir. Onbelleklenir ve yalnizca bagimliliklari degistiginde yeniden hesaplanir; bu da yuksek performans saglar.
<script setup>
import { ref, computed } from 'vue'
// Product list
const products = ref([
{ id: 1, name: 'Laptop', price: 999, inStock: true },
{ id: 2, name: 'Mouse', price: 29, inStock: true },
{ id: 3, name: 'Keyboard', price: 79, inStock: false },
{ id: 4, name: 'Monitor', price: 299, inStock: true }
])
// Active filter
const showOnlyInStock = ref(false)
// computed: automatic filtering based on toggle
// Only recalculates if products or showOnlyInStock changes
const filteredProducts = computed(() => {
if (showOnlyInStock.value) {
return products.value.filter(p => p.inStock)
}
return products.value
})
// computed: total calculation with caching
const totalValue = computed(() => {
return filteredProducts.value.reduce((sum, p) => sum + p.price, 0)
})
// computed: number of displayed items
const productCount = computed(() => filteredProducts.value.length)
</script>
<template>
<div>
<label>
<input type="checkbox" v-model="showOnlyInStock" />
Show only in-stock products
</label>
<p>{{ productCount }} products - Total: ${{ totalValue }}</p>
<ul>
<li v-for="product in filteredProducts" :key="product.id">
{{ product.name }} - ${{ product.price }}
</li>
</ul>
</div>
</template>Hesaplanmis ozellikler, getter ve setter ile yazilabilir de olabilir; bu, cift yonlu donusumlerde kullanislidir.
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// computed with getter AND setter
const fullName = computed({
// Read: combine first and last name
get() {
return `${firstName.value} ${lastName.value}`
},
// Write: split the string into first/last name
set(newValue) {
const parts = newValue.split(' ')
firstName.value = parts[0] || ''
lastName.value = parts.slice(1).join(' ') || ''
}
})
</script>
<template>
<!-- Modifying fullName updates firstName and lastName -->
<input v-model="fullName" placeholder="Full name" />
<p>First name: {{ firstName }}</p>
<p>Last name: {{ lastName }}</p>
</template>watch ve watchEffect ile Izleyiciler
Izleyiciler (watchers), veri degisikliklerine yanit olarak yan etkiler yurutur. Vue 3 iki yaklasim sunar: hassas kontrol icin watch ve otomatik bagimlilik takibi icin watchEffect.
watch, bagimliliklar uzerinde hassas kontrol saglar ve hem eski hem de yeni degeri sunar. watchEffect ise kullanilan tum reaktif bagimliliklarin etkiyi tetiklemesi gereken durumlarda daha basittir.
<script setup>
import { ref, watch, watchEffect } from 'vue'
const searchQuery = ref('')
const searchResults = ref([])
const isLoading = ref(false)
// watch: observes a specific source
// Provides oldValue and newValue, allows debouncing
watch(searchQuery, async (newQuery, oldQuery) => {
console.log(`Search changed from "${oldQuery}" to "${newQuery}"`)
// Don't search if less than 3 characters
if (newQuery.length < 3) {
searchResults.value = []
return
}
isLoading.value = true
try {
// Simulated API call
const response = await fetch(`/api/search?q=${newQuery}`)
searchResults.value = await response.json()
} finally {
isLoading.value = false
}
}, {
// Watcher options
debounce: 300, // Wait 300ms after last keystroke
immediate: false // Don't execute immediately
})
// watchEffect: automatic dependency tracking
// Runs immediately and on every change
watchEffect(() => {
// All refs used here are automatically tracked
console.log(`Current state: ${searchResults.value.length} results`)
console.log(`Loading: ${isLoading.value}`)
})
</script>
<template>
<div>
<input v-model="searchQuery" placeholder="Search..." />
<p v-if="isLoading">Loading...</p>
<ul v-else>
<li v-for="result in searchResults" :key="result.id">
{{ result.title }}
</li>
</ul>
</div>
</template>Ic ice gecmis nesneleri veya dizileri izlemek icin watch ile deep secenegi gereklidir.
<script setup>
import { reactive, watch } from 'vue'
const settings = reactive({
display: {
theme: 'light',
fontSize: 14
},
notifications: {
email: true,
push: false
}
})
// deep: true to observe nested changes
watch(
() => settings.display,
(newDisplay) => {
console.log('Display settings changed:', newDisplay)
// Save to localStorage for example
localStorage.setItem('display', JSON.stringify(newDisplay))
},
{ deep: true }
)
</script>Vue.js / Nuxt.js mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Yeniden Kullanilabilir Composable'lar Olusturmak
Composable'lar, yeniden kullanilabilir reaktif mantigi kapsulleyen fonksiyonlardir. Bu yaklasim, Composition API'nin bilesenler arasi kod paylasimi konusundaki en buyuk guclerinden biridir.
import { ref, watchEffect, toValue } from 'vue'
// Composable for HTTP requests
// Automatically handles loading, errors, and refetching
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const isLoading = ref(false)
// Reusable fetch function
async function fetchData() {
isLoading.value = true
error.value = null
try {
// toValue() allows accepting a ref or a value
const response = await fetch(toValue(url))
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
data.value = await response.json()
} catch (e) {
error.value = e.message
} finally {
isLoading.value = false
}
}
// watchEffect for automatic refetch if url is a ref
watchEffect(() => {
fetchData()
})
// Expose state and methods
return {
data,
error,
isLoading,
refetch: fetchData
}
}Olusturulan composable, herhangi bir bilesende bildirimsel olarak kullanilabilir.
<script setup>
import { useFetch } from '@/composables/useFetch'
// Simple composable usage
const { data: users, isLoading, error, refetch } = useFetch('/api/users')
</script>
<template>
<div>
<button @click="refetch" :disabled="isLoading">
Refresh
</button>
<p v-if="isLoading">Loading users...</p>
<p v-else-if="error" class="error">Error: {{ error }}</p>
<ul v-else-if="users">
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</template>Bir diger ornek, dogrulama iceren form durumu yonetimi icin bir composable'dir.
import { reactive, computed } from 'vue'
// Form management composable
export function useForm(initialValues, validationRules) {
// Form state
const form = reactive({
values: { ...initialValues },
errors: {},
touched: {}
})
// Validate a specific field
const validateField = (field) => {
const rules = validationRules[field]
if (!rules) return true
for (const rule of rules) {
const result = rule(form.values[field])
if (result !== true) {
form.errors[field] = result
return false
}
}
form.errors[field] = null
return true
}
// Validate entire form
const validate = () => {
let isValid = true
for (const field in validationRules) {
if (!validateField(field)) {
isValid = false
}
}
return isValid
}
// computed: form is valid if no errors
const isValid = computed(() => {
return Object.values(form.errors).every(e => !e)
})
// Mark a field as touched (to display errors)
const touch = (field) => {
form.touched[field] = true
validateField(field)
}
// Reset the form
const reset = () => {
form.values = { ...initialValues }
form.errors = {}
form.touched = {}
}
return {
form,
isValid,
validate,
validateField,
touch,
reset
}
}Yasam Dongusu Hook'larinin Yonetimi
Composition API, yasam dongusu hook'larini setup icerisinde cagrilacak fonksiyonlar olarak sunar. Bu hook'lar, bilesenin yasam dongusunun belirli anlarinda kod calistirmayi saglar.
Bellek sizintilarini onlemek icin yan etkiler (zamanlayicilar, olay dinleyicileri, abonelikler) her zaman onUnmounted icerisinde temizlenmelidir.
<script setup>
import {
ref,
onMounted,
onUnmounted,
onBeforeUpdate,
onUpdated
} from 'vue'
const windowWidth = ref(window.innerWidth)
const updateCount = ref(0)
// Handler for resize
const handleResize = () => {
windowWidth.value = window.innerWidth
}
// onMounted: component inserted into DOM
// Ideal for initial API calls and event listeners
onMounted(() => {
console.log('Component mounted')
window.addEventListener('resize', handleResize)
// Example: initialize a third-party library
// chart = new Chart(chartRef.value, config)
})
// onUnmounted: component removed from DOM
// CRITICAL: clean up all side effects
onUnmounted(() => {
console.log('Component unmounted')
window.removeEventListener('resize', handleResize)
// Clean up subscriptions, timers, etc.
// chart?.destroy()
})
// onBeforeUpdate: before DOM update
onBeforeUpdate(() => {
console.log('Before update')
})
// onUpdated: after DOM update
onUpdated(() => {
updateCount.value++
console.log(`DOM updated (${updateCount.value} times)`)
})
</script>
<template>
<div>
<p>Window width: {{ windowWidth }}px</p>
<p>DOM updates: {{ updateCount }}</p>
</div>
</template>Template Ref'leri ve DOM Erisimi
Template ref'leri, DOM ogelarina veya alt bilesen orneklerine dogrudan erisim saglar. Dogrudan manipulasyonun gerektigi durumlarda kullanisli olmaya devam eder.
<script setup>
import { ref, onMounted } from 'vue'
// ref for the DOM element
// Same name as the ref attribute in the template
const inputRef = ref(null)
// Function to focus the input
const focusInput = () => {
// Access to the native DOM element
inputRef.value?.focus()
}
// Auto-focus on mount
onMounted(() => {
focusInput()
})
// Expose the method to parent if needed
defineExpose({
focus: focusInput
})
</script>
<template>
<div>
<!-- The ref attribute binds the element to inputRef -->
<input ref="inputRef" type="text" placeholder="Auto-focused" />
<button @click="focusInput">Refocus</button>
</div>
</template>Props ve Emits ile Ebeveyn-Cocuk Iletisimi
Composition API, defineProps ve defineEmits ile prop ve olay bildirimlerini modernize eder ve daha iyi TypeScript entegrasyonu sunar.
<script setup>
// defineProps: declares expected props
// With default values via withDefaults
const props = withDefaults(defineProps<{
title: string
count?: number
items?: string[]
}>(), {
count: 0,
items: () => []
})
// defineEmits: declares emitted events
// Precise payload typing
const emit = defineEmits<{
(e: 'update', value: number): void
(e: 'submit', data: { title: string; items: string[] }): void
}>()
// Function that emits an event
const handleSubmit = () => {
emit('submit', {
title: props.title,
items: props.items
})
}
const increment = () => {
emit('update', props.count + 1)
}
</script>
<template>
<div class="card">
<h3>{{ title }}</h3>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="handleSubmit">Submit</button>
</div>
</template><script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const currentCount = ref(0)
const items = ref(['Item 1', 'Item 2'])
// Handler for update event
const onUpdate = (newValue) => {
currentCount.value = newValue
}
// Handler for submit event
const onSubmit = (data) => {
console.log('Data submitted:', data)
}
</script>
<template>
<ChildComponent
title="My Component"
:count="currentCount"
:items="items"
@update="onUpdate"
@submit="onSubmit"
/>
</template>Vue.js / Nuxt.js mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Provide ve Inject ile Bagimlilik Enjeksiyonu
Uzak bilesenler arasinda prop drilling olmadan veri paylasimi icin Vue 3, provide ve inject mekanizmasini sunar.
<script setup>
import { provide, ref, readonly } from 'vue'
// Global theme state
const theme = ref('light')
// Function to toggle theme
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
// provide: makes available to descendants
// readonly prevents direct modification by children
provide('theme', readonly(theme))
provide('toggleTheme', toggleTheme)
</script>
<template>
<div :class="theme">
<slot />
</div>
</template><script setup>
import { inject } from 'vue'
// inject: retrieves value provided by an ancestor
// Default value if not found
const theme = inject('theme', 'light')
const toggleTheme = inject('toggleTheme', () => {})
</script>
<template>
<div>
<p>Current theme: {{ theme }}</p>
<button @click="toggleTheme">
Switch to {{ theme === 'light' ? 'dark' : 'light' }}
</button>
</div>
</template>Sonuc
Vue 3 Composition API, Vue uygulama kodunun duzenlenmesi icin guclu ve esnek bir yaklasim sunar. Hatirlanmasi gereken temel kavramlar:
- ref ilkel degerler icin, reactive karmasik nesneler icin
- computed otomatik onbellekleme ile turetilmis degerler icin
- watch ve watchEffect reaktif yan etkiler icin
- Composable'lar bilesenler arasi mantik cikarimi ve yeniden kullanimi icin
- Fonksiyonel yasam dongusu hook'lari (
onMounted,onUnmountedvb.) - provide/inject prop drilling olmadan bagimlilik enjeksiyonu icin
Bu yaklasim, mukemmel TypeScript entegrasyonu sunarken bakimi kolay ve test edilebilir uygulamalar olusturmayi kolaylastirir. Bir sonraki adim, asenkron composable'lar ve global durum yonetimi icin Pinia entegrasyonu gibi ileri duzey kaliplari kesfetmek olabilir.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
