Comparison between the general method / calculated attribute and the monitoring attribute watch in vuejs

Posted by networkthis on Wed, 12 Jan 2022 22:31:53 +0100

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

  1. Initialization values a and B are two numbers
  2. 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

  1. 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
  2. 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)
  3. 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)
  4. 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

  1. Monitor the specified properties through the $watch() or watch configuration of the vm object
  2. 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

  1. If it is clear which data you want to monitor, write watch when preaching the concept instance
  2. 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
  3. When the monitored property changes, the callback function will be called automatically for related operations
  4. The monitored property must exist for monitoring
  5. 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

  1. Do not write arrow functions for vue managed functions
  2. 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

  1. 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
  2. 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

  1. The functions managed by vue should be written as ordinary functions, so that the point of this is the vm or component instance object
  2. 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

Comparison between the common method / calculated attribute and the monitoring attribute watch in the original -vuejs

Topics: Javascript Vue.js watch