Merhaba arkadaşlar, bugün başımı çok ağrıtan ve Vue.js'in temel prensiplerinden birini bana acımasızca öğreten bir debug hikayemi anlatacağım. Uzun süredir kullandığım computed property'lerin aslında nasıl "saf" olması gerektiğini, bu kuralı çiğnediğimde neler yaşadığımı paylaşacağım.
Karşılaştığım Tuhaf Davranış
Bir kullanıcı listesi component'ı üzerinde çalışıyordum. Kullanıcıları filtrelemek, sıralamak ve bu işlem sonunda filtrelenmiş listeyi harici bir analytics servisine göndermek istiyordum. "Tamam," dedim, "hem hesapla hem de gönder, işte computed property tam bu iş için!" İlk versiyonum şöyleydi:
Bu kodu yazdığımda her şey çalışıyor gibiydi. Filtre değiştiğinde liste anında güncelleniyor, analytics'e de veri gidiyordu. Ta ki, component'ta başka bir reactive property değiştiğinde (mesela basit bir sayaç) filteredUsers computed'inin tekrar çalıştığını ve analytics'e defalarca, gereksiz istek attığını fark edene kadar. Konsol spam'lanmış, analytics kotam bitmek üzereydi. Kafayı yemiştim!
Vue Reactivity Sistemini Anlamak
Sorunun kaynağını anlamak için Vue'un reactivity sistemine döndüm. Computed property'ler, bağımlı oldukları reactive değerler (this.users, this.selectedRole) değiştiğinde otomatik olarak yeniden hesaplanır. Benim computed'im, bu bağımlılıklar dışında, component'ın herhangi bir yerindeki herhangi bir reactive değişiklikte de tetikleniyordu çünkü içinde component state'ini değiştiren (this.sendToAnalytics) bir yan etki (side effect) vardı. Bu, Vue'un computed property'ler için temel bir kuralını ihlal ediyordu: Computed'lar sadece hesaplama yapmalı ve saf (pure) olmalıdır. Yan etkileri olmamalıdır.
Bulduğum Temiz Çözüm
Çözüm, sorumlulukları ayırmaktı. Computed property sadece ve sadece veriyi hesaplayacak, yan etkiyi ise bir watch veya method ile yönetecektim. İşte benim kullandığım en temiz çözüm:
Bu pattern ile, filteredUsers artık sadece bağımlı olduğu değerler değiştiğinde ve gerçekten hesaplama gerektiğinde çalışıyor. Analytics gönderme işlemi de sadece liste gerçekten değiştiğinde, kontrollü bir şekilde yapılıyor.
Çıkarılan Ders ve Best Practice
Bu zorlu debug sürecinden sonra computed property'ler konusunda kesin kurallarım oldu:
1. Computed içine asla this.$emit, axios.post, console.log (debug hariç) veya DOM manipülasyonu koyma.
2. Computed'ları bir değer döndüren saf fonksiyonlar gibi düşün. Aynı girdi için her zaman aynı çıktıyı vermeli.
3. Bir işlem hem hesaplama hem de yan etki içeriyorsa, hemen "Acaba bunu watch veya method ile mi ayırmalıyım?" diye sorgula.
Siz Vue.js'te computed property'lerle ilgili benzer tuzaklara düştünüz mü? Ya da böyle yan etkileri yönetmek için farklı, daha şık bir pattern'iniz var mı? Yorumlarda deneyimlerinizi paylaşın, beraber öğrenelim!
Bir kullanıcı listesi component'ı üzerinde çalışıyordum. Kullanıcıları filtrelemek, sıralamak ve bu işlem sonunda filtrelenmiş listeyi harici bir analytics servisine göndermek istiyordum. "Tamam," dedim, "hem hesapla hem de gönder, işte computed property tam bu iş için!" İlk versiyonum şöyleydi:
JavaScript:
computed: {
filteredUsers() {
// Filtreleme ve sıralama mantığı
const filtered = this.users.filter(user => user.active && user.role === this.selectedRole);
const sorted = filtered.sort((a, b) => a.name.localeCompare(b.name));
// YAN ETKİ: Analytics'e veri gönderme
this.sendToAnalytics('filtered_users_count', sorted.length); // BU SATIR HATA!
return sorted;
}
}
Bu kodu yazdığımda her şey çalışıyor gibiydi. Filtre değiştiğinde liste anında güncelleniyor, analytics'e de veri gidiyordu. Ta ki, component'ta başka bir reactive property değiştiğinde (mesela basit bir sayaç) filteredUsers computed'inin tekrar çalıştığını ve analytics'e defalarca, gereksiz istek attığını fark edene kadar. Konsol spam'lanmış, analytics kotam bitmek üzereydi. Kafayı yemiştim!
Sorunun kaynağını anlamak için Vue'un reactivity sistemine döndüm. Computed property'ler, bağımlı oldukları reactive değerler (this.users, this.selectedRole) değiştiğinde otomatik olarak yeniden hesaplanır. Benim computed'im, bu bağımlılıklar dışında, component'ın herhangi bir yerindeki herhangi bir reactive değişiklikte de tetikleniyordu çünkü içinde component state'ini değiştiren (this.sendToAnalytics) bir yan etki (side effect) vardı. Bu, Vue'un computed property'ler için temel bir kuralını ihlal ediyordu: Computed'lar sadece hesaplama yapmalı ve saf (pure) olmalıdır. Yan etkileri olmamalıdır.
Çözüm, sorumlulukları ayırmaktı. Computed property sadece ve sadece veriyi hesaplayacak, yan etkiyi ise bir watch veya method ile yönetecektim. İşte benim kullandığım en temiz çözüm:
JavaScript:
computed: {
// SAF computed: Sadece veriyi hesaplar, başka hiçbir şey yapmaz.
filteredUsers() {
const filtered = this.users.filter(user => user.active && user.role === this.selectedRole);
return filtered.sort((a, b) => a.name.localeCompare(b.name));
}
},
watch: {
// Yan etki, computed'in değerini izleyen bir watcher'a taşındı.
filteredUsers(newList, oldList) {
// Gereksiz tetiklenmeleri önlemek için kontrol
if (newList.length !== oldList?.length) {
this.sendToAnalytics('filtered_users_count', newList.length);
}
}
},
methods: {
sendToAnalytics(event, data) {
// Analytics gönderme mantığı
console.log(`Analytics: ${event} - ${data}`);
}
}
Bu pattern ile, filteredUsers artık sadece bağımlı olduğu değerler değiştiğinde ve gerçekten hesaplama gerektiğinde çalışıyor. Analytics gönderme işlemi de sadece liste gerçekten değiştiğinde, kontrollü bir şekilde yapılıyor.
Bu zorlu debug sürecinden sonra computed property'ler konusunda kesin kurallarım oldu:
1. Computed içine asla this.$emit, axios.post, console.log (debug hariç) veya DOM manipülasyonu koyma.
2. Computed'ları bir değer döndüren saf fonksiyonlar gibi düşün. Aynı girdi için her zaman aynı çıktıyı vermeli.
3. Bir işlem hem hesaplama hem de yan etki içeriyorsa, hemen "Acaba bunu watch veya method ile mi ayırmalıyım?" diye sorgula.
Siz Vue.js'te computed property'lerle ilgili benzer tuzaklara düştünüz mü? Ya da böyle yan etkileri yönetmek için farklı, daha şık bir pattern'iniz var mı? Yorumlarda deneyimlerinizi paylaşın, beraber öğrenelim!