background
In vue, you can use common methods to implement the same functional requirements, or you can use the calculated attribute and the watch attribute. At the beginning, there were some puzzles about their use
As for when to use the method, when to use the calculated attribute and the watch attribute, it is often a headache
Different methods are suitable for the corresponding scenarios. The following is the content of this chapter
Demand scenario
Enter a and B and sum them. When the sum result meets the specified conditions, specify the age stage of the result
result <= 6 => It's a child result > 6 && result <= 17 => "He is a teenager" result > 17 && result <=40 => "He is a young man" result > 40 && result <=65 => "He is a middle-aged man" result > 65 && result <=100 =>"He is an old man" isNaN(result))=> "The information you entered is incorrect" result > 100 "year,He's over a hundred years old,Are you still from earth"
Specific effect demonstration
<img src="https://static01.imgkr.com/temp/b07a314d7f9242feb3fbd1e61f0726ed.gif" class="medium-zoom lazy">
requirement analysis
- Initialization values a and B are two numbers
- Calculate the result of the sum of the two numbers and make the corresponding logical judgment
Method 1 - implemented in template mode
In the vue template, simple logical judgment can be made in the interpolation expression
The specific code is as follows:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>01-Common method implementation</title> <style> .box { margin-left: 35%; margin-top: 100px; } </style> </head> <body> <div id="root"> <div class="box"> A: <input type="number" v-model:value="A" /> <span>+</span> B: <input type="number" v-model:value="B" /> <button>=</button> <span>{{ parseInt(A)+parseInt(B) }}</span><br /><br /> <div> result: {{ A}} +{{B}} = {{parseInt(A)+parseInt(B)}} <span v-if="(parseInt(A)+parseInt(B))<=6">He is a child</span> <span v-else-if="(parseInt(A)+parseInt(B)) > 6 && (parseInt(A)+parseInt(B)) <= 17">He is a teenager</span> <span v-else-if="(parseInt(A)+parseInt(B)) > 17 && (parseInt(A)+parseInt(B)) <= 40">He is a young man</span> <span v-else-if="(parseInt(A)+parseInt(B)) > 40 && (parseInt(A)+parseInt(B)) <= 65">He is a middle-aged man</span> <span v-else-if="(parseInt(A)+parseInt(B)) > 65 && (parseInt(A)+parseInt(B)) <= 100">He is an old man</span> <span v-else-if="(parseInt(A)+parseInt(B)) > 100">year,He's over a hundred years old,Are you still from earth</span> <span v-else="isNaN(parseInt(A)+parseInt(B))">The information you entered is incorrect</span> </div> </div> </div> <script src="./js/vue.js"></script> <script> const vm = new Vue({ el: "#root", data() { return { A: "4", B: '5', } } }) </script> </body> </html>
It is very convenient to use expressions in vue templates to handle simple logic, but it is only suitable for simple operations. If the logic is very complex, maintaining templates will become cumbersome and not intuitive
proposal
For complex logic, you should use methods or calculate the computed attribute
Additional expansion
Why is the value of data written as a function rather than an object?
briefly
When a component is defined, data must be declared as a function that returns an initial data object, because the component may be used to create multiple instances
That is, in many pages, defined components can be reused in multiple pages
If data is a pure broken object, all instances will share and reference the same data object. Modifying data in any component instance will affect all component instances
If data is a function, every time a new instance is created, the data function is called to return a new replica data object of the initial data.
In this way, each time a component is reused, a new data will be returned, which is similar to creating a private data space for each component instance, so that the instances of each component are independent and do not affect each other
Method 2 - implemented using common methods
The sample code is as follows: define the method (function) in methods, and directly call the method (function name ()) in vue template
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>01-Implemented using common methods</title> <style> .box { margin-left: 35%; margin-top: 100px; } </style> </head> <body> <div id="root"> <div class="box"> A: <input type="number" v-model:value="A" /> <span>+</span> B: <input type="number" v-model:value="B" /> <button>=</button> <span>{{ addResult() }}</span><br /><br /> <div> result: {{ A}} +{{B}} = {{addResult()}} {{outPut()}} </div> </div> </div> <script src="./js/vue.js"></script> <script> const vm = new Vue({ el: "#root", data() { return { A: "4", B: '5', } }, methods: { addResult() { return parseInt(this.A)+parseInt(this.B) }, outPut() { const result = parseInt(this.A)+parseInt(this.B); if(result <= 6) { return "He is a child" }else if(result > 6 && result <= 17) { return "He is a teenager" } else if(result > 17 && result <=40) { return "He is a young man" }else if(result > 40 && result <=65) { return "He is a middle-aged man" }else if(result > 65 && result <=100) { return "He is an old man" }else if(isNaN(result)) { return "The information you entered is incorrect" }else { return "year,He's over a hundred years old,Are you still from earth" } } }, }) </script> </body> </html>
The above is the method defined in methods to implement
matters needing attention
When using the normal method, whenever the method is triggered, the page will be re rendered and the method function will be executed. It has no cache
If there is a calculation attribute with high performance overhead, it needs to traverse a large array and do a lot of calculations, and the calculation attribute has other dependencies. If there is no cache and the attribute is not calculated, the getter for collecting the attribute will be executed continuously. If there is no cache, use a method to replace it
Method 3 - implemented using the calculated attribute
In the vue instance configuration option, add the calculated attribute. The value is an object, and add the corresponding calculated attribute
The value obtained by calculating the attribute is the previously cached calculation result and will not be executed multiple times
The example code is as follows
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>02-Use calculated properties computed realization</title> <style> .box { margin-left: 35%; margin-top: 100px; } </style> </head> <body> <div id="root"> <div class="box"> A: <input type="number" v-model:value="A" /> <span>+</span> B: <input type="number" v-model:value="B" /> <button>=</button> <span>{{ addResult }}</span><br /><br /> <div> result: {{ A}} +{{B}} = {{addResult}} {{outPut}} </div> </div> </div> <script src="./js/vue.js"></script> <script> const vm = new Vue({ el: "#root", data() { return { A: "4", B: '5', } }, computed: { addResult: { // Original writing style get() { return parseInt(this.A)+parseInt(this.B) } }, outPut: { get() { const result = parseInt(this.A)+parseInt(this.B); if(result <= 6) { return "He is a child" }else if(result > 6 && result <= 17) { return "He is a teenager" } else if(result > 17 && result <=40) { return "He is a young man" }else if(result > 40 && result <=65) { return "He is a middle-aged man" }else if(result > 65 && result <=100) { return "He is an old man" }else if(isNaN(result)) { return "The information you entered is incorrect" }else { return "year,He's over a hundred years old,Are you still from earth" } } } } }) </script> </body> </html>
reminder
Once it is determined that the calculation attribute only reads (gets) without modifying the set, and it is determined that read-only does not change, the simplified form can be used, as shown below
// Others are omitted, as shown above computed: { // Once it is determined that the calculation property only reads (gets) without modifying the set, the short form can be used //If it is determined that read-only is not changed, the simplified form can be used addResult() { return parseInt(this.A)+parseInt(this.B) }, outPut() { const result = parseInt(this.A)+parseInt(this.B); if(result <= 6) { return "He is a child" }else if(result > 6 && result <= 17) { return "He is a teenager" } else if(result > 17 && result <=40) { return "He is a young man" }else if(result > 40 && result <=65) { return "He is a middle-aged man" }else if(result > 65 && result <=100) { return "He is an old man" }else if(isNaN(result)) { return "The information you entered is incorrect" }else { return "year,He's over a hundred years old,Are you still from earth" } } } })
matters needing attention
- The result of calculating the attribute does not need to be mounted under data for data initialization. It can be used directly in vue template without adding parentheses to calculate the attribute name (), which is different from the call of ordinary methods
- Putting too much logic into the template will make the template too heavy, difficult to maintain, and not intuitive (simple logic can be processed in the template)
- For complex logic, you can use the calculation attribute (the getter function of the calculation attribute has no side effects, but you can also use the method, but the calculation attribute has a large amount of calculation, can cache the calculation results, and has higher performance. It consumes performance by frequently calling methods, parsing templates and rendering pages)
- The calculated attributes are cached based on their responsive dependencies. They will be re evaluated only when the relevant responsive dependencies change. Compared with the call of ordinary methods, whenever the re rendering is triggered, the call method executes the function and parses the vue template
Method 4 - use the watch listening attribute to implement
- Monitor the specified properties through the $watch() or watch configuration of the vm object
- When the property changes, the callback function is called automatically to calculate within the function
Specific example code
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>03-Listening properties watch Method implementation</title> <style> .box { margin-left: 35%; margin-top: 100px; } </style> </head> <body> <div id="root"> <div class="box"> A: <input type="number" v-model:value="A" /> <span>+</span> B: <input type="number" v-model:value="B" /> <button>=</button> <span>{{ addResult }}</span><br /><br /> <div> result: {{ A}} +{{B}} = {{addResult}} {{outPut}} </div> </div> </div> <script src="./js/vue.js"></script> <script> const vm = new Vue({ el: "#root", data() { return { A: "4", B: '5', addResult: '', outPut: '' } }, watch: { A: { immediate: true, // Let the handler call during initialization. The default is false handler(newVal,oldVal) { console.log("A The data has changed","abreast of the times:",newVal,"old:",oldVal); this.addResult = parseInt(newVal)+parseInt(this.B) const result = parseInt(this.addResult); if(result <= 6) { this.outPut = "He is a child" }else if(result > 6 && result <= 17) { this.outPut = "He is a teenager" } else if(result > 17 && result <=40) { this.outPut = "He is a young man" }else if(result > 40 && result <=65) { this.outPut = "He is a middle-aged man" }else if(result > 65 && result <=100) { this.outPut = "He is an old man" }else if(isNaN(result)) { this.outPut= "The information you entered is incorrect" }else { this.outPut = "year,He's over a hundred years old,Are you still from earth" } } }, B: { immediate: true, handler(newVal, oldVal) { console.log("B The data has changed","abreast of the times",newVal,"old",oldVal); this.addResult = parseInt(this.A)+parseInt(newVal); const result = parseInt(this.addResult); if(result <= 6) { this.outPut = "He is a child" }else if(result > 6 && result <= 17) { this.outPut = "He is a teenager" } else if(result > 17 && result <=40) { this.outPut = "He is a young man" }else if(result > 40 && result <=65) { this.outPut = "He is a middle-aged man" }else if(result > 65 && result <=100) { this.outPut = "He is an old man" }else if(isNaN(result)) { this.outPut= "The information you entered is incorrect" }else { this.outPut = "year,He's over a hundred years old,Are you still from earth" } } } }, }) </script> </body> </html>
When the response data does not need immediate: true and deep: true, it can be abbreviated. The above watch is equivalent as follows
be careful
If it is written in a short form, the configuration options cannot be written
// Other parts are omitted, as shown above watch: { // Equivalent to the following A(newVal) { // The newVal parameter here refers to the latest value of the current monitoring attribute. You can write one or two (newVal,oldVal) this.addResult = parseInt(newVal)+parseInt(this.B) const result = parseInt(this.addResult); if(result <= 6) { this.outPut = "He is a child" }else if(result > 6 && result <= 17) { this.outPut = "He is a teenager" } else if(result > 17 && result <=40) { this.outPut = "He is a young man" }else if(result > 40 && result <=65) { this.outPut = "He is a middle-aged man" }else if(result > 65 && result <=100) { this.outPut = "He is an old man" }else if(isNaN(result)) { this.outPut= "The information you entered is incorrect" }else { this.outPut = "year,He's over a hundred years old,Are you still from earth" } }, B(newVal) { console.log("B The data has changed","abreast of the times",newVal,"old"); this.addResult = parseInt(this.A)+parseInt(newVal); const result = parseInt(this.addResult); if(result <= 6) { this.outPut = "He is a child" }else if(result > 6 && result <= 17) { this.outPut = "He is a teenager" } else if(result > 17 && result <=40) { this.outPut = "He is a young man" }else if(result > 40 && result <=65) { this.outPut = "He is a middle-aged man" }else if(result > 65 && result <=100) { this.outPut = "He is an old man" }else if(isNaN(result)) { this.outPut= "The information you entered is incorrect" }else { this.outPut = "year,He's over a hundred years old,Are you still from earth" } } }, })
Of course, Vue provides the $watch instance method, which can also be written as follows
<script> const vm = new Vue({ el: "#root", data() { return { A: "4", B: '5', addResult: '', outPut: '' } }, // Equivalent to the following vm.$watch('A',{ immediate: true, // Let the handler call during initialization. The default is false. If you don't write this, the handler function will not be called for the first time, which will not meet your expectations handler(newVal,oldVal) { console.log("A The data has changed","abreast of the times:",newVal,"old:",oldVal); this.addResult = parseInt(newVal)+parseInt(this.B) const result = parseInt(this.addResult); if(result <= 6) { this.outPut = "He is a child" }else if(result > 6 && result <= 17) { this.outPut = "He is a teenager" } else if(result > 17 && result <=40) { this.outPut = "He is a young man" }else if(result > 40 && result <=65) { this.outPut = "He is a middle-aged man" }else if(result > 65 && result <=100) { this.outPut = "He is an old man" }else if(isNaN(result)) { this.outPut= "The information you entered is incorrect" }else { this.outPut = "year,He's over a hundred years old,Are you still from earth" } } }) // Equivalent to monitoring the B attribute as shown below vm.$watch('B', { immediate: true, // During initialization, call some handler functions. If you do not write this, the handler function will not be called for the first time handler(newVal, oldVal) { console.log("B The data has changed","abreast of the times",newVal,"old",oldVal); this.addResult = parseInt(this.A)+parseInt(newVal); const result = parseInt(this.addResult); if(result <= 6) { this.outPut = "He is a child" }else if(result > 6 && result <= 17) { this.outPut = "He is a teenager" } else if(result > 17 && result <=40) { this.outPut = "He is a young man" }else if(result > 40 && result <=65) { this.outPut = "He is a middle-aged man" }else if(result > 65 && result <=100) { this.outPut = "He is an old man" }else if(isNaN(result)) { this.outPut= "The information you entered is incorrect" }else { this.outPut = "year,He's over a hundred years old,Are you still from earth" } } }) </script>
The watch attribute is a very useful attribute. If you need to monitor some data, compare and transform old and new data, and do some logical operations when certain conditions are met, watch can monitor the attributes under data and the attribute of calculation results
About the timing of writing watch and $Watch
- If it is clear which data you want to monitor, write watch when preaching the concept instance
- If you don't know which data to monitor when creating an instance and which data will be monitored according to some user behaviors, you can use the $watch API
- When the monitored property changes, the callback function will be called automatically for related operations
- The monitored property must exist for monitoring
- There are two ways to monitor data. One is to pass in the watch configuration option when instantiating Vue objects, and the other is VM$ watch
Depth monitoring in watch
The above is to directly listen to the attributes directly mounted under data. What should we do when we want to listen to a single attribute under an object? As shown below
const vm = new Vue({ el: '#root', data() { return { info: { name: 'itclanCoder', // Want to listen to a single attribute under the info object age: 4 } } }, // Listen for changes in an attribute in a multi-level structure watch: { 'info.name': { console.log("info Below name Attribute changed"); } } })
In Vue, the change of the internal value of the object is not monitored by default. If you want to monitor the change of each attribute under the object (that is, monitor the multi-level structure), you can set to enable the deep: true configuration, as shown below
const vm = new Vue({ el: '#root', data() { return { info: { name: 'itclanCoder', age: 4 } } }, // Listen for changes in an attribute in a multi-level structure watch: { info: { immediate: true, // The handler function is called immediately upon initialization deep: true, // Open depth monitoring handler() { console.log("name and age It all changed"); } } } }) // Equivalent to the following expression vm.$watch('info',{ immediate: true, // The handler function is called immediately upon initialization deep: true, // Open depth monitoring handler() { console.log("name and age It all changed"); } })
Note: tip
vm.$watch('info',function(newVal,oldVal) { // Arrow functions cannot be written here, but ordinary functions should be written, otherwise there will be problems with the binding of this console.log("New value",newVal,"Old value",oldVal); },{ immediate:true, deep: true })
When changing (not replacing) an object or array, the old value will be the same as the new value because their references point to the same object / array. Vue does not keep a copy of the value before the change
warning
- Do not write arrow functions for vue managed functions
- Get and set in the calculation property cannot be written as arrow functions
watch supports asynchronous tasks to maintain data
Key contents:
When asynchronous or expensive operations need to be performed when data changes, watch is the most useful method, while computed can't do it (relying on the return value)
watch: { // Equivalent to the following A(newVal) { // The newVal parameter here refers to the latest value of the current monitoring attribute. You can write one or two (newVal,oldVal) setTimeout(() => { // The callback function here cannot be written as an ordinary function, otherwise this will point to window and there will be problems this.addResult = parseInt(newVal)+parseInt(this.B) const result = parseInt(this.addResult); if(result <= 6) { this.outPut = "He is a child" }else if(result > 6 && result <= 17) { this.outPut = "He is a teenager" } else if(result > 17 && result <=40) { this.outPut = "He is a young man" }else if(result > 40 && result <=65) { this.outPut = "He is a middle-aged man" }else if(result > 65 && result <=100) { this.outPut = "He is an old man" }else if(isNaN(result)) { this.outPut= "The information you entered is incorrect" }else { this.outPut = "year,He's over a hundred years old,Are you still from earth" } },2000) }, }, })
Sometimes, we want to delay how long to implement the corresponding logic, so watch can effectively start an asynchronous task
<img src="https://static01.imgkr.com/temp/f48c1fac362c4028a05fe6cb9b9a204d.png" class="medium-zoom lazy">
It can be concluded from the figure above
- computed: it monitors the dependency value. When the dependency value remains unchanged, it will directly read the cache for reuse. When the dependency value changes, it will be recalculated
- watch: it monitors the attribute value. Whenever the attribute changes, it will trigger the execution callback function to perform a series of operations
But computed doesn't work. Computed depends on the return value. watch depends on your own code to modify it
There is no way to start asynchronous tasks in calculation properties. It must be executed synchronously to maintain data, but watch can
When both watch and computed can be implemented, it is recommended to use computed, but when some asynchronous tasks are to be processed and implemented, it is necessary to use watch
The difference between computed and watch
The functions that can be completed by computed and watch can be completed
The functions that can be completed by watch may not be completed by computed. For example, watch can perform an operation
Two important small differences
- The functions managed by vue should be written as ordinary functions, so that the point of this is the vm or component instance object
- All functions not managed by vue (timer callback function, ajax callback function and Promise callback function) should be written as arrow function, so that the point of this is the vm or component instance object
summary
The same function is implemented in vue. For simple logic functions, templates can be used, followed by methods (but they do not have the ability to cache data). If the logic is very complex and needs to cache data, the calculation attribute can be used, and the watch attribute can also be implemented
In normal development, we give priority to the use of computing attributes. It can be seen that it is simpler and more convenient. However, if you want to perform asynchronous tasks, you have to use watch. If computed can do it, watch can do it, but vice versa