The two important features of Vue framework, computed and watch, are similar before. Today, let's talk about why there are computed and watch? And their usage scenarios? When to use computed? When to use watch? What's the difference between them? Please read today's article
Why is there a computed attribute
In Vue, we can easily map data to DOM, and it is also convenient to use expressions in the template, but if too much logic is written in the template, it will make the template difficult to maintain.
<template> <div :title="msg"> {{ msg }} {{ msg.length }} </div> </template> <script> export default { data() { return { msg: 'Hello World', } }, } </script>
For example, we have a product list. We display different status information by the number of products. Please see the following example:
<template> <div> {{ productList.length > 0 ? 'in stock' : 'The goods are temporarily out of stock'}} </div> </template> <script> export default { data() { return { productList: [{ title: 'JS Advanced programming', }], } }, } </script>
At this point, the template is no longer simple. We must look at the expression in the template to know that its state depends on productList.length. Then, if the same state is required in multiple places in the template, we must write multiple copies of the same code, and the template will become worse.
Therefore, when we need to deal with some complex logic of responsive data, we should use the calculated attribute. Take the following example:
<template> <div> {{ productStatus }} </div> </template> <script> export default { data() { return { productList: [{ title: 'JS Advanced programming', }], } }, computed: { productStatus() { return this.productList.length > 0 ? 'in stock' : 'The goods are temporarily out of stock' }, }, } </script>
Here, we declare a calculated property productStatus.
If we change the value of the productList array, we will see that the productStatus will be modified accordingly.
We can bind the calculated attribute to the template just as we bind the normal attribute. Vue internally knows that productStatus depends on productList, so when the productList changes, all bindings that depend on productStatus will also be updated. The most ingenious thing is that we have created this dependency by declaration, and the getter function that calculates the attribute has no side effects, making it easier to test.
Calculate attribute VS method
The above code can also be implemented in the form of methods, for example:
<template> <div> {{ getProductStatus() }} </div> </template> <script> export default { data() { return { productList: [{ title: 'JS Advanced programming', }], } }, methods: { getProductStatus() { return this.productList.length > 0 ? 'in stock' : 'The goods are temporarily out of stock' }, }, } </script>
We define the same logic as a function instead of calculating attributes. You can see that the results of the two methods are exactly the same. So what's the difference?
Here comes the important. Please take a closer look. The differences are:
The calculation attribute is cached based on the response dependency. The calculation attribute will be re evaluated only when the response dependency changes. That is, if the productList will never change, the previous calculated results will be returned every time the productStatus calculation attribute above is accessed, and there is no need to execute the function again. In other words, the calculation attribute will have the function of caching data.
For example, the calculated properties of the following code will never be updated because Date.now() is not a responsive dependency:
computed: { now() { return Date.now() } }
Compared with calculating attributes, the method is always executed again whenever re rendering is triggered. If there are N method calls in the template, it will be called N times.
So what are the advantages of computing attribute caching?
Suppose we have a computational property list with high performance overhead, which needs to traverse a huge array and do a lot of calculations. Then we may have other computational properties that depend on the list. If there is no cache, we will inevitably execute the getter of the list many times!
In this way, the performance will be much better.
Setter for Calculating Properties
The default calculation property is only getter, but you can also set a setter, as follows:
// ... 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] } } } // ...
When we run this.fullName = 'Xiaoshuai', the setter will be called, and this.firstName and this.lastName will be updated accordingly.
I rarely use this setter in the project. When will you use it? Welcome to provide materials.
Watch
Computing properties can actually meet our needs most of the time, but Vue still provides us with a more free way to monitor data changes, that is, watch.
So when to use the watch?
As defined in Vue official documents
This approach is most useful when asynchronous or expensive operations need to be performed when data changes.
In the improved example, suppose we want to query the list of products according to keywords
<div id="watch-example"> <p> Please enter the name of the product <input v-model="keywords" /> </p> <p v-if="getting">Data acquisition in progress..</p> <p v-else v-for="item in productList">{{ item.title }}</p> </div>
<script> const watchExampleVM = Vue.createApp({ data() { return { keywords: '', getting: false, productList: [] } }, watch: { // This function will trigger if the keywords change keywords(newKeywords, oldKeywords) { if (newKeywords.indexOf('?') > -1) { this.getAnswer() } } }, methods: { getProductList() { this.getting = true; axios .get('/api/products') .then(response => { this.productList = response.data }) .finally(() => { this.getting = false; }) } } }).mount('#watch-example') </script>
In this example, using watch allows us to call the asynchronous API to obtain data, and use the getting attribute to control the data state, which cannot be achieved by using the calculation attribute.
Abuse Watch
The watch provided by Vue allows us to monitor the changes of any response data. When some data needs to change with the changes of other data, it is easy to use the watch. However, in fact, a better way is to use the declarative calculation attribute rather than the command watch callback. Please experience the following example:
<div id="demo">{{ fullName }}</div>
const vm = Vue.createApp({ data() { return { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' } }, watch: { firstName(val) { this.fullName = val + ' ' + this.lastName }, lastName(val) { this.fullName = this.firstName + ' ' + val } } }).mount('#demo')
The above code is imperative and repetitive. Compare with the version of the calculated attribute:
const vm = Vue.createApp({ data() { return { firstName: 'Foo', lastName: 'Bar' } }, computed: { fullName() { return this.firstName + ' ' + this.lastName } } }).mount('#demo')
Through the above comparison, it is found that calculating attributes is much better.
last
Refer to Vue official documentation: Compute properties and listeners
Learn more front-end knowledge together. Wechat searches [Xiaoshuai's Programming Notes] and updates them every day