خاصیت Computed و Watchers

اصول آموزش vuejs

تاریخ : پنج شنبه 11 مهر 1398

بررسی (Computed Properties)

استفاده از عبارات در قالب ها بسیار راحت است ، اما بیشتر برای عملیات ساده مورد استفاده قرار می گیرند. قرار دادن منطق بیش از حد در الگوها نگه داری آن را سخت می کند. مثلا:

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

در این مثال ، قالب دیگر ساده و اعلامی نیست. وقتی می خواهید پیام معکوس شده را در الگوی خود بیش از یک بار بگنجانید این مشکل بدتر می شود.

به همین دلیل برای هر منطق پیچیده ، باید از یک خاصیت computed (محاسبه شده) استفاده کنید.

مثال ساده :

<div id="example">
  <p>Original message: " {{ message }}"</p>
  <p>Computed reversed message: " {{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // a computed getter
    reversedMessage: function () {
      // `this` points to the vm instance
      return this.message.split('').reverse().join('')
    }
  }
})

نتیجه:

Original message: "{{ message }}"

Computed reversed message: "{{ reversedMessage }}"

در اینجا ما یک خاصیت computed به نام reversedMessage اعلان کرده ایم. تابعی که ارائه دادیم به عنوان تابع getter برای ویژگی vm.reversedMessage استفاده خواهد شد:

console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'

می توانید console را باز کرده و با مثال vm خود کار کنید. مقدار vm.reversedMessage همیشه به مقدار vm.message بستگی دارد.

شما می توانید داده ها را به خاصیت های computed در قالب ها دقیقاً همانند یک ویژگی معمولی متصل کنید. Vue می داند که vm.reversedMessage به vm.message بستگی دارد ، بنابراین هر اتصالی که وابسته به vm.reversedMessage باشد را زمانیکه vm.message تغییر کند به روز رسانی می کند. بهترین قسمت کار این است که ما این رابطه وابستگی را بصورت اعلانی ایجاد کرده ایم و این باعث می شود تست و درک آن آسان تر شود.


مقایسه Computed Caching با Methods

شاید متوجه شده باشید که با استفاده از method در عبارت می توانیم به نتیجه مشابه برسیم:

<p>Reversed message: "{{ reverseMessage() }}"</p>
// in component
methods: {
  reverseMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

به جای یک خاصیت computed ، می توانیم همان عملکرد را به عنوان یک method تعریف کنیم. نتایج برای هردو یکسان است. با این حال ، تفاوت در این است که خاصیت های computed بر اساس وابستگی واکنشی آنها ذخیره موقت (cached) می شوند. یک خاصیت computed فقط در صورت تغییر برخی از وابستگی های واکنشی آن ، دوباره ارزیابی می شود. این بدان معنی است تا زمانی که پیام تغییر نکرده ، دسترسی چندگانه به خاصیت computed (محاسبه شده) reversedMessage بلافاصله بدون نیاز به اجرای دوباره عملکرد ، نتیجه قبلاً محاسبه شده را برمی گرداند.

این همچنین بدان معنی است که ویژگی computed زیر هرگز به روز نمی شود ، زیرا ()Date.now یک وابستگی واکنشی نیست:

computed: {
  now: function () {
    return Date.now()
  }
}

در مقایسه ، method هر زمان که یک رندر دوباره(re-render) رخ دهد عملکرد را انجام می دهد.

چرا ما نیاز به ذخیره سازی موقت محاسبات داریم؟ تصور کنید که ما یک خاصیت computed به نام A با محاسبات سنگین داشته باشیم که نیاز به حلقه زدن در یک Array عظیم و انجام محاسبات زیادی دارد. بنابراین ممکن است ما خاصیت های computed دیگری داشته باشیم که به نوبه خود به A بستگی داشته باشند . بدون ذخیره کردن ، چند بار بیشتر از حد لازم A را اجرا می کنیم! در مواردی که نمی خواهید حافظه پنهانی داشته باشید ، به جای آن از method استفاده کنید.


مقایسه Computed با watch

Vue روشی عمومی تر برای مشاهده و واکنش به تغییرات داده ها در یک نمونه فراهم می کند به نام خاصیت watch. چنانچه داده هایی دارید که بر اساس برخی داده های دیگر تغییر می کنند ، استفاده از watch می تواند بسیار وسوسه انگیز باشد به خصوص اگر قبلا با محیط کار AngularJS کار کرده باشید. با این حال ، اغلب ایده بهتر استفاده از خاصیت computed می باشد. این مثال را در نظر بگیرید:

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

کد بالا ضروری و تکراری است.حال آن را با یک نسخه computed مقایسه کنید:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

خیلی بهتر است ، اینطور نیست؟


افزودن Setter به Computed

ویژگی های Computed به صورت پیش فرض فقط دارای getter می باشند ، اما می توانید در صورت نیاز به آن یک setter نیز اضافه نمایید:

// ...
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]
    }
  }
}
// ...

اکنون وقتی vm.fullName = 'John Doe' را اجرا کردید ،setter فراخوانی می شود و vm.firstName و vm.lastName به همین ترتیب به روز می شوند.


بررسی Watchers

در حالی که خاصیت های computed در بیشتر موارد مناسب تر هستند ، مواقعی وجود دارد که یک watcher (مشاهده گر) سفارشی می تواند ضرورت داشته باشد. به همین دلیل است که Vue یک روش عمومی تر برای واکنش به تغییرات داده ها از طریق watch ارائه می دهد. بیشترین کاربرد زمانیست که بخواهید عملیات ناهمزمان (asynchronous) در پاسخ به تغییر داده ها ، داشته باشید.

برای مثال:

<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
<!-- Since there is already a rich ecosystem of ajax libraries    -->
<!-- and collections of general-purpose utility methods, Vue core -->
<!-- is able to remain small by not reinventing them. This also   -->
<!-- gives you the freedom to use what you're familiar with.      -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    // whenever question changes, this function will run
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },
  created: function () {
    // _.debounce is a function provided by lodash to limit how
    // often a particularly expensive operation can be run.
    // In this case, we want to limit how often we access
    // yesno.wtf/api, waiting until the user has completely
    // finished typing before making the ajax request. To learn
    // more about the _.debounce function (and its cousin
    // _.throttle), visit: https://lodash.com/docs#debounce
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods: {
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      this.answer = 'Thinking...'
      var vm = this
      axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = 'Error! Could not reach the API. ' + error
        })
    }
  }
})
</script>

در این حالت ، استفاده از گزینه Watch به ما این امکان را می دهد تا عملیاتی asynchronous (ناهمزمان) (دسترسی به یک API) را انجام دهیم ، تعداد دفعات عملیات را می توان محدود و حالت های واسطه را تنظیم کنیم تا پاسخ نهایی را بدست آوریم. هیچ یک از این موارد با خاصیت computed امکان پذیر نیست.

علاوه بر گزینه Watch ، می توانید از vm.$watch API نیز استفاده کنید.


منابع مورد مطالعه جهت جمع آوری این مطلب:
https://vuejs.org/v2/guide/computed.html


نظرات