Vue2 (2): calculated properties, monitoring properties

Article directory

  • 1. Computed properties
    • 1.1 Use interpolation syntax and methods to splice names
    • 1.2 Use computed properties to splice names
    • 1.3 Abbreviation of computed properties
    • 1.4 Summary of computed properties
  • 2. Monitoring attributes-watch
    • 2.1 Weather case foreshadowing
    • 2.2 Lead to monitoring attributes
      • (1). Pass in the watch configuration when new Vue
      • (2). Monitor through vm.$watch
    • 2.3 Deep monitoring
    • 2.4 Abbreviated form of monitoring
  • 3.watch vs computed
    • 3.1 Comparing cases of splicing names

1. Calculated properties

As the name suggests, a computed property is a calculated property. The English name is computed.
This should be distinguished from the things in data and methods. The attributes in data and the functions in methods will be put into the vm intact, but the things in computed are objects, and what is finally put into the vm is The name of this object, and the value is the return value in get.

1.1 Use interpolation syntax and methods to splice names

Using interpolation syntax, if it is simple, it will be fine. If it is complicated, there will be a lot of logic inside, which is ugly.
Using methods is good, but there is a problem, that is, it will be called once every time fullName is used, and there is too much cache.

<!-- Prepare a container -->
<div id="root">
    Last name: <input type="text" v-model="firstName"><br><br>
    Name: <input type="text" v-model="lastName"><br><br>
    <!-- Splicing names to achieve "Zhang-San" linkage -->
    <!-- The first implementation: using interpolation syntax (slice method of string, closing the left and opening the right to intercept the string), troublesome -->
    Name: <span>{<!-- -->{<!-- -->firstName.slice(0,3)}}-{<!-- -->{<!-- -->lastName} }</span><br><br>


    <!-- The second implementation: using methods, they will be called once every time they are used, while calculated properties only need to be called the first time -->
    Name: <span>{<!-- -->{<!-- -->fullName()}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName()}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName()}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName()}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName()}}</span>
</div>

<script>
    const vm = new Vue({<!-- -->
        el: '#root',
        data: {<!-- -->
            firstName: 'Zhang',
            lastName: '三'
        },
        methods: {<!-- -->
            fullName() {<!-- -->
                console.log('call')
                return this.firstName + '-' + this.lastName;
            }
        }
    })
</script>

1.2 Use computed attributes to splice names

? 1. There is no caching problem. It will not be called again after the first call, saving memory.
? 2. The properties in the calculated properties should be written in the form of objects. Each object has getters and setters.
? 3. FullName is actually the calculated value of firstName and lastName, which is directly transferred to vm and becomes a property of vm.
? 4. When someone reads fullName, get is called and the return value is used as the value of fullName.
? 5. When is set called? It is called when fullName is modified. There is a chain reaction here. I manually modify vm.fullName, which causes firstName and lastName to be modified. The Vue template re-parses, the page is refreshed, and the modification of firstName and lastName causes get is re-executed (the dependent data has changed), and the new fullName is returned. Calculated properties are calculated all the time, so if you want to change fullName, you must change the values it depends on.

<!-- Prepare a container -->
<div id="root">
    Last name: <input type="text" v-model="firstName"><br><br>
    Name: <input type="text" v-model="lastName"><br><br>
    <!-- Splicing names to achieve "Zhang-San" linkage -->

    <!-- Use calculated properties, call them only once, very nice, save memory -->
    Name: <span>{<!-- -->{<!-- -->fullName}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName}}</span><br><br>
    Name: <span>{<!-- -->{<!-- -->fullName}}</span>
</div>

<script>
    const vm = new Vue({<!-- -->
        el: '#root',
        data: {<!-- -->
            firstName: 'Zhang',
            lastName: '三'
        },
        computed: {<!-- -->
            //The properties in the calculated properties should be written in the form of objects. Each object has getters and setters.
            //This fullName is actually the stuff obtained by firstName and lastName after some calculations, and is directly given to the vm.
            fullName: {<!-- -->
                //What is the use of get? When someone reads fullName, get is called and the return value is used as the value of fullName.
                //When is get called? 1. Reading fullName for the first time 2. The data used in get has changed
                get() {<!-- -->
                    console.log('fullName was read');
                    // console.log(this);//this here is vm
                    return this.firstName + '-' + this.lastName;
                },

                //When is set called? Called when fullName is manually modified
                //There is a chain reaction here. I manually modified vm.fullName, causing firstName and lastName to be modified, and the Vue template was re-parsed.
                //The page is refreshed, and the modification of firstName and lastName causes get to be re-executed (the dependent data has changed), and the new fullName is returned.
                //Calculated properties Calculated properties are always calculating, so if you want to change fullName, you must change the values it depends on.
                set(val) {<!-- -->
                    let arr = val.split('-');
                    this.firstName = arr[0];
                    this.lastName = arr[1];
                }
            }
        }
    })
</script>

1.3 Abbreviation of computed properties

You can use the abbreviation when only considering reading and not modifying:

<!-- Prepare a container -->
<div id="root">
    Last name: <input type="text" v-model="firstName"><br><br>
    Name: <input type="text" v-model="lastName"><br><br>
    <!-- Splicing names to achieve "Zhang-San" linkage -->

    <!-- Use calculated properties, only call them once, very nice, save memory -->
    Name: <span>{<!-- -->{<!-- -->fullName}}</span>
</div>

<script>
    const vm = new Vue({<!-- -->
        el: '#root',
        data: {<!-- -->
            firstName: 'Zhang',
            lastName: '三'
        },
        computed: {<!-- -->
            fullName() {<!-- -->
                console.log('fullName was read');
                return this.firstName + '-' + this.lastName;
            }

        }
    })
</script>

1.4 Summary of Computed Properties

Definition: The property to be used does not exist and must be calculated from the existing properties in Vue.
Principle: The bottom layer uses the getters and setters provided by the Objcet.defineproperty method.
When will the get function be executed?

(1). It will be executed once when reading for the first time.
(2). It will be called again when the dependent data changes.
Advantages: Compared with methods implementation, there is an internal caching mechanism (reuse), which is more efficient and easy to debug.

Remark:
1. Computed attributes will eventually appear on the vm. They can be read and used directly without parentheses. They are different from methods.
2. If the calculated attribute is to be modified, then a set function must be written to respond to the modification, and the set must cause the data relied upon for calculation to change, which is critical.

2. Monitoring attributes-watch

2.1 Weather case foreshadowing

Implement click button to switch weather

  • Writing method 1: Use interpolation syntax and ternary expressions
  • Writing method 2: Use calculated properties
<div id="root">
    <!-- Implement click button to switch weather -->
    <!-- Writing method 1: Use interpolation syntax and ternary expressions -->
    <h2>The weather is very {<!-- -->{<!-- -->isHot ? 'hot': 'cold'}}</h2>

    <!-- Writing method 2: Use calculated properties -->
    <h2>The weather is very {<!-- -->{<!-- -->info}}</h2> today
    <!-- When binding events: @xxx="yyy" yyy can write some simple statements, but it is best not to do this -->
    <!-- <button @click="isHot = !isHot">Switch weather</button>-->
    <button @click='change'>Click to switch weather</button>
</div>
<script>
    new Vue({<!-- -->
        el: '#root',
        data: {<!-- -->
            isHot: true
        },
        methods: {<!-- -->
            change() {<!-- -->
                this.isHot = !this.isHot;
            }
        },
        computed: {<!-- -->
            info() {<!-- -->
                //Note that isHot here needs to add this
                return this.isHot ? 'hot' : 'cold';
            }
        },
    })
</script>

2.2 Export monitoring attributes

1. There is a handler function in the monitored attribute. The handler function is called when the monitored attribute (such as isHot) is modified.
2. The handler function passes two parameters, which are: (value after modification, value before modification)
3. The monitored attributes must exist before monitoring can be performed! ! It can be either an attribute in data (isHot) or a calculated attribute (info)
4. Two ways of writing surveillance:

(1).Pass in watch configuration when new Vue

<div id="root">
    <!-- Implement click button to switch weather -->
    <!-- Writing method 1: Use interpolation syntax and ternary expressions -->
    <h2>The weather is very {<!-- -->{<!-- -->isHot ? 'hot': 'cold'}}</h2>

    <!-- Writing method 2: Use calculated properties -->
    <h2>The weather is very {<!-- -->{<!-- -->info}}</h2> today
    <!-- When binding events: @xxx="yyy" yyy can write some simple statements, but it is best not to do this -->
    <!-- <button @click="isHot = !isHot">Switch weather</button>-->
    <button @click='change'>Click to switch weather</button>
</div>
<script>
    new Vue({<!-- -->
        el: '#root',
        data: {<!-- -->
            isHot: true
        },
        methods: {<!-- -->
            change() {<!-- -->
                this.isHot = !this.isHot;
            }
        },
        computed: {<!-- -->
            info() {<!-- -->
                //Note that isHot here needs to add this
                return this.isHot ? 'hot' : 'cold';
            }
        },
        watch: {<!-- -->
            isHot: {<!-- -->
                immediate: true, //call handler once during initialization
                //Call the handler function when isHot is modified
                handler(newVal, oldVal) {<!-- -->
                    console.log('isHot has been modified', 'before the change was' + oldVal, 'after the change was' + newVal);
                }
            },
            //Watch can not only monitor the attributes in data, but also monitor calculated attributes.
            info: {<!-- -->
                immediate: true, //call handler once during initialization
                //Call the handler function when isHot is modified
                handler(newVal, oldVal) {<!-- -->
                    console.log('info has been modified', 'before the change was' + oldVal, 'after the change was' + newVal);
                }
            }
        }
    })
</script>

(2). Monitor through vm.$watch

const vm = new Vue({<!-- -->
    el: '#root',
    data: {<!-- -->
        isHot: true
    },
    methods: {<!-- -->
        change() {<!-- -->
            this.isHot = !this.isHot;
        }
    },
    computed: {<!-- -->
        info() {<!-- -->
            //Note that isHot here needs to add this
            return this.isHot ? 'hot' : 'cold';
        }
    }
})

vm.$watch('isHot', {<!-- -->
immediate: true, //call handler once during initialization
//Call the handler function when isHot is modified
handler(newVal, oldVal) {<!-- -->
console.log('isHot has been modified', 'before the change was' + oldVal, 'after the change was' + newVal);
}
})
\t
vm.$watch('info', {<!-- -->
immediate: true, //call handler once during initialization
//Call the handler function when isHot is modified
handler(newVal, oldVal) {<!-- -->
console.log('info has been modified', 'before the change was' + oldVal, 'after the change was' + newVal);
}
})

2.3 Deep Monitoring

(1). The watch in Vue does not monitor changes in the internal value of the object by default (one layer, if it is monitored, the object address will remain unchanged).
(2). Configure deep:true to monitor changes in the internal value of the object (multiple layers).
Remark:
(1).Vue itself can monitor changes in the internal values of objects, but the watch provided by Vue cannot by default! If you want, you have to manually turn on deep:true
(2). When using watch, decide whether to use in-depth monitoring based on the specific structure of the data.

 <!-- Prepare a container -->
    <div id="root">
        <h3>The value of a is: {<!-- -->{<!-- -->numbers.a}}</h3>
        <button @click="numbers.a + + ">Click me to achieve a + 1</button>
        <h3>The value of b is: {<!-- -->{<!-- -->numbers.b}}</h3>
        <button @click="numbers.b + + ">Click me to achieve b + 1</button>
        <h3>I have to force the number to change</h3>
        <button @click="numbers = {a:666, b:999}">Click me to change numbers</button>
    </div>
    <script>
        const vm = new Vue({<!-- -->
            el: '#root',
            data: {<!-- -->
                isHot: true,
                numbers: {<!-- -->
                    a: 1,
                    b: 2
                }
            },
            watch: {<!-- -->
                //Monitor the change of an attribute in the multi-level structure and wrap it in quotation marks (previously without quotation marks, it was abbreviated form)
                'numbers.a': {<!-- -->
                    handler() {<!-- -->
                        console.log('a was changed');
                    }
                },
                //If you write like this, the handler will not be executed even if ab changes, because it means monitoring the numbers attribute.
                //And this attribute value is an object. As long as the address of the numbers object remains unchanged, it will not change unless the violent method written in the div above is used.
                 numbers: {<!-- -->
                     handler() {<!-- -->
                         console.log('numbers changed');
                     }
                 },

                //But if you add a deep, you can monitor the change of a certain attribute in the multi-level structure. If ab changes, the numbers will also change.
                numbers: {<!-- -->
                    deep: true,
                    handler() {<!-- -->
                        console.log('numbers changed');
                    }
                },
            }
        })
    </script>

Abbreviated form of 2.4 monitoring

Form 1:

 watch: {<!-- -->
                //Complex writing:
                 isHot: {<!-- -->
                    //immediate: true, //call the handler once during initialization
                    //Call the handler function when isHot is modified
                    handler(newVal, oldVal) {<!-- -->
                        console.log('isHot has been modified', 'before the change was' + oldVal, 'after the change was' + newVal);
                     }
                },
                
//abbreviation
                // If there are no other configuration items (deep, immediate, etc.) in the handler function, it can be abbreviated to the following form
                isHot(newVal, oldVal) {<!-- -->
                    console.log('isHot has been modified', 'before the change was' + oldVal, 'after the change was' + newVal);
                }
            }

Form 2:

 //Complex form
vm.$watch('isHot', {<!-- -->
//Call the handler function when isHot is modified
handler(newVal, oldVal) {<!-- -->
console.log('isHot has been modified', 'before the change was' + oldVal, 'after the change was' + newVal);
}
})
//short form
vm.$watch('isHot',function (newVaule,oldVaule) {<!-- -->
console.log(newVaule,oldVaule);
})

3.watch vs computed

3.1 Comparative cases of splicing names

<!-- Prepare a container -->
<div id="root">
    Last name: <input type="text" v-model="firstName"><br><br>
    Name: <input type="text" v-model="lastName"><br><br>
    <!-- Splicing names to achieve "Zhang-San" linkage -->
    Name: <span>{<!-- -->{<!-- -->fullName}}</span>
</div>

Computed properties:

const vm = new Vue({<!-- -->
    el: '#root',
    data: {<!-- -->
        firstName: 'Zhang',
        lastName: '三'
    },
    computed: {<!-- -->
        fullName() {<!-- -->
            console.log('fullName was read');
            return this.firstName + '-' + this.lastName;
        }

    }
})

Monitor properties:

 const vm = new Vue({<!-- -->
        el: '#root',
        data: {<!-- -->
            firstName: 'Zhang',
            lastName: '三',
            fullName: 'Zhang-san'
        },
        watch: {<!-- -->
            firstName: {<!-- -->
                handler(newVal, oldVal) {<!-- -->
                    this.fullName = newVal + '-' + this.lastName;
                }
            },
            lastName: {<!-- -->
                handler(newVal, oldVal) {<!-- -->
                    this.fullName = this.firstName + '-' + newVal;
                }
            },
        }
    })

It seems that calculated properties are simpler, so are calculated properties really awesome? I think watch is more awesome because it can be asynchronous.
For example, if I manually change firstName, do I have to wait a second before changing fullName?
Using watch attributes, we can write:

watch: {<!-- -->
    firstName: {<!-- -->
        handler(newVal, oldVal) {<!-- -->
            setTimeout(() => {<!-- -->
                this.fullName = newVal + '-' + this.lastName;
            }, 1000);
        }
    }
}

But computed is useless, because it waits for one second before returning? This can’t be fixed

##3.2 Summary

1. Watch can complete all the functions that computed can complete.
2. The functions that watch can complete may not be completed by computed. For example, watch can perform asynchronous operations.

Two important little principles!
1. The functions managed by Vue are best written as ordinary functions, so that this points to the vm or component instance object.
Even more awesome because it can be asynchronous.
For example, if I manually change firstName, do I have to wait a second before changing fullName?
Using watch attributes, we can write:

watch: {<!-- -->
    firstName: {<!-- -->
        handler(newVal, oldVal) {<!-- -->
            setTimeout(() => {<!-- -->
                this.fullName = newVal + '-' + this.lastName;
            }, 1000);
        }
    }
}

But computed is useless, because it waits for one second before returning? This can’t be fixed

##3.2 Summary

1. Watch can complete all the functions that computed can complete.
2. The functions that watch can complete may not be completed by computed. For example, watch can perform asynchronous operations.

Two important little principles!
1. The functions managed by Vue are best written as ordinary functions, so that this points to the vm or component instance object.
2. All functions that are not managed by Vue (timer callback functions, ajax callback functions, etc., Promise callback functions) are best written as arrow functions, so that this points to the vm or component instance object.