Vue 3'te Performans ve Render Optimizasyonu Teknikleri
Vue 3 uygulamalarında performans, kullanıcı deneyimini doğrudan etkileyen kritik bir faktördür. Bu yazıda, Vue 3'ün sunduğu performans optimizasyon tekniklerini, render optimizasyonlarını ve büyük veri setleriyle çalışırken kullanabileceğimiz stratejileri inceleyeceğiz.
Virtual DOM ve Render Optimizasyonu
Vue 3'ün yeni render motoru, Virtual DOM implementasyonunda önemli iyileştirmeler getirdi. İşte bu optimizasyonları maksimum verimle kullanmanın yolları:
Template Compilation Optimizasyonları
<template>
<!-- Kötü Kullanım -->
<div v-if="show">
<ExpensiveComponent v-for="item in list" :key="item.id" />
</div>
<!-- İyi Kullanım -->
<template v-if="show">
<ExpensiveComponent v-for="item in list" :key="item.id" />
</template>
</template>
<script setup>
import { defineComponent } from 'vue'
// Gereksiz wrapper div'den kaçınmak için Fragment kullanımı
defineComponent({
name: 'OptimizedList'
})
</script>
Computed Properties ve Memorization
import { computed, ref } from 'vue'
const list = ref([/* büyük veri listesi */])
const searchQuery = ref('')
// Kötü Kullanım: Her render'da yeniden hesaplanır
const filteredList = () => {
return list.value.filter(item =>
item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
)
}
// İyi Kullanım: Sadece bağımlılıklar değiştiğinde hesaplanır
const optimizedFilteredList = computed(() => {
return list.value.filter(item =>
item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
)
})
// Çok parametreli hesaplamalar için custom memorization
function useMemoize(fn: Function) {
const cache = new Map()
return (...args: any[]) => {
const key = JSON.stringify(args)
if (cache.has(key)) return cache.get(key)
const result = fn(...args)
cache.set(key, result)
return result
}
}
Büyük Listelerin Optimizasyonu
Büyük veri setleriyle çalışırken performansı korumak için virtual scrolling ve chunk rendering teknikleri:
Virtual Scrolling Implementation
<template>
<div class="virtual-list" :style="containerStyle">
<div class="virtual-list-inner" :style="innerStyle">
<template v-for="item in visibleItems" :key="item.id">
<div class="list-item" :style="{ height: itemHeight + 'px' }">
{{ item.content }}
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted, onUnmounted } from 'vue'
const props = defineProps<{
items: Array<{ id: number; content: string }>
itemHeight: number
}>()
const scrollTop = ref(0)
const containerHeight = ref(0)
const containerRef = ref<HTMLElement | null>(null)
// Görünür öğeleri hesapla
const visibleItems = computed(() => {
const start = Math.floor(scrollTop.value / props.itemHeight)
const count = Math.ceil(containerHeight.value / props.itemHeight)
return props.items.slice(start, start + count + 1)
})
const containerStyle = computed(() => ({
height: `${containerHeight.value}px`,
overflow: 'auto',
position: 'relative'
}))
const innerStyle = computed(() => ({
height: `${props.items.length * props.itemHeight}px`,
transform: `translateY(${Math.floor(scrollTop.value / props.itemHeight) * props.itemHeight}px)`
}))
const handleScroll = () => {
if (containerRef.value) {
scrollTop.value = containerRef.value.scrollTop
}
}
onMounted(() => {
if (containerRef.value) {
containerHeight.value = containerRef.value.clientHeight
containerRef.value.addEventListener('scroll', handleScroll)
}
})
onUnmounted(() => {
if (containerRef.value) {
containerRef.value.removeEventListener('scroll', handleScroll)
}
})
</script>
<style scoped>
.virtual-list {
position: relative;
overflow: auto;
}
.virtual-list-inner {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.list-item {
padding: 8px;
border-bottom: 1px solid #eee;
}
</style>
Chunk Rendering
Büyük listeleri parçalar halinde render etme:
import { ref, onMounted } from 'vue'
export function useChunkRendering<T>(items: T[], chunkSize = 100) {
const renderedItems = ref<T[]>([])
const currentChunk = ref(0)
const renderNextChunk = () => {
const start = currentChunk.value * chunkSize
const chunk = items.slice(start, start + chunkSize)
renderedItems.value = [...renderedItems.value, ...chunk]
currentChunk.value++
}
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
renderNextChunk()
}
})
onMounted(() => {
renderNextChunk() // İlk chunk'ı render et
})
return {
renderedItems,
renderNextChunk
}
}
// Kullanımı
const items = ref([/* büyük veri listesi */])
const { renderedItems, renderNextChunk } = useChunkRendering(items.value)
Component Render Optimizasyonu
defineAsyncComponent ile Lazy Loading
import { defineAsyncComponent } from 'vue'
// Ağır componenti lazy load et
const HeavyComponent = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
)
// Yükleme durumu ve hata yönetimi ile
const HeavyComponentWithOptions = defineAsyncComponent({
loader: () => import('./components/HeavyComponent.vue'),
loadingComponent: LoadingSpinner,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})
shallowRef ve shallowReactive Kullanımı
Derin reaktivite gerektirmeyen durumlarda performans optimizasyonu:
import { shallowRef, shallowReactive } from 'vue'
// Sadece üst seviye değişiklikleri izle
const state = shallowReactive({
user: {
profile: {
name: 'John',
settings: { /* derin nested veriler */ }
}
}
})
// Büyük veri yapıları için shallow referans
const bigData = shallowRef({
// Çok büyük nested veri yapısı
})
// Sadece referans değiştiğinde yeniden render
bigData.value = newBigData
Event Handling Optimizasyonu
Event Debouncing ve Throttling
import { ref } from 'vue'
export function useDebounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
) {
let timeout: NodeJS.Timeout
return (...args: Parameters<T>) => {
clearTimeout(timeout)
timeout = setTimeout(() => fn(...args), delay)
}
}
// Kullanımı
const searchQuery = ref('')
const debouncedSearch = useDebounce((query: string) => {
// API çağrısı yap
fetchSearchResults(query)
}, 300)
// Template'de
// <input v-model="searchQuery" @input="debouncedSearch(searchQuery)" />
Memory Leak Önleme
Composition API ile Cleanup
import { onUnmounted, onMounted } from 'vue'
export function useEventListener(
target: Window | HTMLElement,
event: string,
callback: EventListener
) {
onMounted(() => target.addEventListener(event, callback))
// Cleanup
onUnmounted(() => target.removeEventListener(event, callback))
}
// WebSocket bağlantıları için cleanup örneği
export function useWebSocket(url: string) {
const ws = new WebSocket(url)
onUnmounted(() => {
ws.close()
})
return ws
}
Performance Monitoring
Custom Performance Metrics
import { onMounted, onUpdated } from 'vue'
export function usePerformanceMonitoring(componentName: string) {
let startTime: number
onMounted(() => {
startTime = performance.now()
// Component mount süresini ölç
requestAnimationFrame(() => {
const mountTime = performance.now() - startTime
console.log(`${componentName} mount time:`, mountTime)
})
})
onUpdated(() => {
const updateTime = performance.now() - startTime
console.log(`${componentName} update time:`, updateTime)
})
}
Sonuç
Vue 3'te performans optimizasyonu, birçok farklı teknik ve yaklaşımın bir kombinasyonudur. Virtual DOM optimizasyonları, computed properties'in doğru kullanımı, büyük listelerin verimli yönetimi ve component lazy loading gibi tekniklerin uygun şekilde kullanılması, uygulamanızın performansını önemli ölçüde artırabilir.
Bu optimizasyon tekniklerini uygularken, her zaman önce performans ölçümü yapmanız ve gerçekten bir optimizasyona ihtiyaç olup olmadığını belirlemeniz önemlidir. Erken optimizasyon, kod karmaşıklığını artırabilir ve bakım maliyetlerini yükseltebilir.
İlgili Etiketler: #Vuejs #Performance #Optimization #VirtualDOM #CompositionAPI #Frontend #JavaScript #WebDevelopment