Vue.js Computed, Method, Watch Kullanımı

Vue.js yazı serisi çerçevesinde, direktiflerin (directives) ardından, daha kapsamlı ve kapsayıcı bir konu olan computed property (hesaplanan özellik), methods (metod/yordam/yöntem) ve watch (izleme) detaylarına geçebiliriz. Öncelikle metot (method) ile ilgili bir kısa not eklemek istiyorum. Metotlar , uygulamalarda bir işlem gerçekleştirmek için kullanılan kod parçacıklarıdır1 2.

Computed, Methods ve Watch

Hesaplanan özellikler (computed property / computed prop) ve metotlar (method), benim için temel mantığını kavramanın biraz zaman aldığı konular oldular. Neyseki Vue.js dokümantasyonu3 4 ve eğitimlerdeki örnekler süreci (computed property, methods ve watch ilişkileri, benzeştikleri alanlar ve farklılıkları gibi) oldukça kolaylaştırdılar. Gelelim computed konusuna. Vue Lifecycle dahilinde yer alan computed, render işlemi tamamlandıktan sonraki andır ve computed adında bir metod oluşturup kullanırız. Metotları ise component içeriğinde, istediğimiz anda çağırabiliriz. Lifecycle konusuna ayrıca değineceğiz. Şimdilik, computed ile ilgili detaylarda bu hususu aklınızda tutmanızda fayda var.

Vue.JS Computed Property Kullanımı

Mustache syntax kullanımına sıklıkla yer vermeye çalıştım ve hatta bazı örnekler içerisinde işlemler de barındıracak kullanımlara yer verdim ve bu yöntemin çok önerilmediğini de ekledim. Genel bir toparlama yapmak adına ilgili işleme tekrar bakalım.

<div id="example">
  {{ message.split('').reverse().join('') }}
</div>

Yukarıdaki kullanımda, message alındığında bir dizi işlemden geçirilmekte; split() ile parçalara ayrılmakta, reverse() ile karakterlerin yerleri değiştirilmekte ve join() ile yerleri değiştirilen karakterler birleştirilmekte. Sonuç olarak, message tanımı, örneğin data { message: 'Hello' } olsun, olleH haline getirilerek ekranda gösterilmekte. Kapsamlı bir yapı içerisinde, template oluştururken veya düzenlerken bu ve benzeri kullanımlar kontrolü güç bir hale gelebilir. Bu tür durumlarda, gerekli işlemler computed içerisinde kolaylıkla tanımlanabilirler. Aynı işlemi şimdi yeniden ele alalım.

// Vue v2.x
var app = new Vue({
  el: '#message',
  data: {
    message: 'Hello'
  },
  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  }
});

// Vue v3.x
const app = {
  data() {
    return {
      message: 'Hello'
    }
  },
  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('');
    }
  }
}
Vue.createApp(app).mount('#message');

// ya da

Vue.createApp({
  data() {
    return {
      message: 'Hello'
    }
  },
  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('');
    }
  }
}).mount('#message');

Çıktımızın aynı olmasına karşın, önceki kullanımdan farklı olarak HTML etiketi içerisinde message yerine reversedMessage kullanmamız gerekmekte. Bu sayede, artık veri değiştikçe ilgili alanın da değiştiğini görebiliriz.

Computed property ile template içerisinde normal property işlemlerinde olduğu gibi data-bind işlemleri yapmak mümkün. Çünkü, Vue örnek üzerinden anlatmak gerekirse, computed olarak tanımlı reversedMessage ile message öğesi birbirine bağlı. Bu sayede message değiştiğinde reversedMessage ve ona bağlı olan tüm alanlar da güncellenir. Bu bağımlılık bildirimsel olarak (declaratively) gerçekleştirilir.

Unutmadan, computed property ön tanımlı olarak getter tanımlıdır. Ancak, ihtiyaç halinde setter tanımlanabilmektedir; { get: Function, set: Function }

computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

// ya da

computed: {
  fullName: {
    // getter
    get() {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set(newValue) {
      const names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

Console üzerinden fullName = 'John Doe' tanımı yaptığımızda setter firstName ve lastName için de güncelleme geçecektir.

Computed Property İle Methods Karşılaştırması

v-model direktifi açıklamasında da bahsi geçtiği üzere computed ve methods‘un işlev anlamında örtüştüğü noktalar olsalar da temel ve aslında önemli bir farklılık da mevcut5. computed property bağlı olduğu değişkeni ekranda sunarken ön belleğe (cache) de alır ve bu değişken değişmediği sürece tekrar hesaplama yapmaz. method kullanımında ise hesaplama yinelenir ve son değer ön bellekleme olmadan ekrana yansıtılır. Özetle, method re-render gerçekleşen her durumda yeniden çalıştırılır, ancak computed kapsamındaki işlemler yinelenmez. Şimdi, yukarıdaki örneğimizi method ile tekrar oluşturalım.

<p>{{ reverseMessage() }}</p>
veya
<p v-text="reverseMessage()"></p>
methods: {
  reverseMessage() {
    return this.message.split('').reverse().join('')
  }
}

Tanımlandığı alan dışında, yazımda herhangi bir farklılık olmadı ve ekrana yansıyan ifade de aynı.

Computed Property İle Watched Property Karşılaştırması

VueJS, data değişikliklerini gözlemlemek ve bunlara tepki vermek için watch properties olarak ifade edilen daha genel bir yola daha sahiptir. Lifecycle içerisinde watch component var olduğu sürede, o component içeriğindeki datalarla ilgili değişiklikleri yakalamızı sağlar. Örneğin, bir veri bir başka veriye bağlı ise ve bağlı olduğu veride değişiklik söz konusu olmuşsa watch (watcher / watched prop) kullanımı tercih edilebilir. Şöyle bir örneğimiz olsun;

<div id="app">
 <p v-html="welcome"></p>
 <ul>
  <li>Name: <strong>{{ firstName }}</strong></li>
  <li>Surname: <strong>{{ lastName }}</strong></li>
  <li>Full Name: <strong>{{ fullName }}</strong></li>
 </ul>
</div>

<script>
// Vue 2.x
var app = new Vue({
 el: '#app',
 data: {
  message: `It's a New Day!`,
  firstName: 'John',
  lastName: 'Doe',
  fullName: 'John Doe'
 },
 computed: {
  welcome() {
   return 'Hello' + ' <strong>' + this.fullName + '</strong>, ' + this.message
  }
 },
 watch: {
  message(val) {
   this.message = val
  },
  firstName(val) {
   this.fullName = val + ' ' + this.lastName
  },
  lastName(val) {
   this.fullName = this.firstName + ' ' + val
  }
 }
});

// Vue 3.x
const app = Vue.createApp({
  data() {
    return {
      message: `It's a New Day!`,
      firstName: 'John',
      lastName: 'Doe',
      fullName: 'John Doe'
    }
  },
  computed: {
    welcome() {
      return 'Hello' + ' <strong>' + this.fullName + '</strong>, ' + this.message
    }
  },
  watch: {
    message(val) {
      this.message = val
    },
    firstName(val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName(val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
}).mount('#app');
</script>

Console üzerinden app.lastName = 'Wick' tanımı yapalım. Bu durumda hem fullName hem de message içeriğinin değiştiğini görebilirsiniz. Watch alanını temizleyip aynı işlemi gerçekleştirdiğinizde sadece ilgili veri alanı güncellenecektir6.

Özetlemek gerekirse, computed properties diğer verilerden türetilmiş yeni veriler oluşturmak istendiğinde öne çıkmaktadır7. Bu verileri dönüştürmek (transform), filtrelemek (filter) ya da değiştirmek (manipulate) istediğimizde rahatlıkla kullanabiliriz. Computed properties her zaman bir değer döndürmek (return) ve eş zamanlı olmak durumundadır. Diğer yandan, bir bileşenin (component) prop aldığını ve bu prop içeriğinin her değişmesi durumunda bir AJAX isteği gerçekleştirilmesi gerektiğini düşünün. Bu durumda Watch property ile verideki değişikliği izlemek çok daha isabetli bir karar olacaktır8. Özetlemek gerekirse asynchronous ve bütçeli operasyonlardaki veri değişikliklerinin takibinde watch değerlendirilebilir bir seçenektir9 10.