FE_Vue学习笔记 - 计算属性 & 监视属性

1 计算属性

1.1 由methods引入计算属性

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。

<body>
<div id="name-methods">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{firstName.slice(0,3)}}-{{secondName}} </span>
</div>
<script type="text/javascript">
    new Vue({
        el: '#name-methods',
        data() {
            return {
                firstName: 'zhao',
                secondName: 'shuai-lc'
            }
        },
        methods: {}
    })
</script>
</body>
</html>

抽象成如下方式更为合适:

<body>
<div id="name-methods">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{ fullName() }}</span>
</div>
<script type="text/javascript">
    new Vue({
        el: '#name-methods',
        data() {
            return {
                firstName: 'zhao',
                secondName: 'shuai-lc@inspur.com'
            }
        },
        methods: {
            fullName() {
                return this.firstName + '-' + this.secondName
            }
        }
    })
</script>
</body>
</html>

1.2 计算属性的理解

  1. 定义:要用的属性不存在,要通过已有的属性计算得来。

  2. 原理:底层借助了Object.defineproperty方法提供的getter和setter。

  3. get函数什么时候执行?
    1)初次读取时会执行一次。
    2)当依赖的数据发生改变时会被再次调用。

  4. 备注:
    1)计算属性最终会出现在vm上,直接读取使用即可。
    2)如果计算属性要被修改,那必须直接写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。

<body>
<div id="name-calculate">
    姓:<input type="text" v-model="firstName"><br/>
    名:<input type="text" v-model="secondName"><br/>
    全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
    Vue.config.productionTip = false
    new Vue({
        el: '#name-calculate',
        data: {
            firstName: 'zhao',
            secondName: 'shuai-lc@inspur.com'
        },
        methods: {},
        computed: {
            fullName: {
                // 当有人读取fullName的时候调用get()
                // 1)初次读取时会执行一次。
                // 2)当依赖的数据发生改变时会被再次调用。
                get() {
                    return this.firstName + ' - ' + this.secondName
                },
                // 当有人修改fullName时候调用set()
                set(value) {
                    const list = value.split('-');
                    this.firstName = list[0]
                    this.secondName = list[1]
                }
            }
        }
    })

</script>
</body>
</html>

注意的是 :

  1. vm._data里面是没有计算属性的
    在这里插入图片描述
  2. 计算属性中get函数的this,Vue已经维护好了,并把getter中的this指向调成了vm。

在Vue中,计算属性和在methods中定义的方法都可以用来处理数据,但它们之间有一些重要的区别:

  • 缓存机制:计算属性具有缓存机制,只有在其依赖项发生变化时才会重新计算。这意味着只要依赖项不变,多次访问计算属性会立即返回之前缓存的结果,而不会再次执行计算逻辑。而在methods中定义的方法每次调用时都会重新执行,即使传递的参数没有变化。
  • 响应式更新:计算属性是响应式的,它们依赖于其他响应式数据(如props、data属性等),并在依赖项发生变化时自动更新。计算属性的更新是隐式的,你不需要显式地调用它们。而methods中定义的方法不是响应式的,它们不会自动更新,你需要显式地调用它们来触发更新。
  • 用途:计算属性通常用于处理与数据相关的逻辑,并返回一个新的值,这个值可以通过模板进行访问和展示。计算属性适用于对数据的处理和转换,例如拼接字符串、过滤列表等。而在methods中定义的方法通常用于执行副作用操作或复杂的业务逻辑,例如向服务器发送请求、处理用户输入等。

1.3 计算属性简写

确定了只读,不可以修改,才能使用简写方式。

<body>
<div id="root">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span> <br/><br/>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false

    const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        computed: {
            // 简写
            fullName: function () {
                return this.firstName + '-' + this.lastName
            }
        }
    })
</script>
</body>
</html>

1.4 计算属性的缓存

计算属性是基于其依赖项进行缓存的,这意味着只有当计算属性的依赖项发生变化时,计算属性才会重新计算。这种缓存机制可以提高性能,避免不必要的计算。

以下是一个简单的示例,说明计算属性的缓存机制:

new Vue({  
  data: {  
    firstName: 'Foo',  
    lastName: 'Bar',  
  },  
  computed: {  
    fullName: function () {  
      console.log('计算fullName');  
      return this.firstName + ' ' + this.lastName;  
    }  
  }  
})

在这个例子中,当我们访问fullName时,会打印出“计算fullName”,并且计算结果的缓存将被存储起来。当我们再次访问fullName时,由于依赖项firstName和lastName没有发生变化,因此不会重新计算,而是直接从缓存中返回结果。

1.5 计算属性的依赖项

计算属性的依赖项是指计算属性所依赖的数据属性或其他计算属性。当依赖项发生变化时,计算属性将重新计算。

以下是一个示例,展示计算属性的依赖项:

new Vue({  
  data: {  
    firstName: 'Foo',  
    lastName: 'Bar',  
  },  
  computed: {  
    fullName: function () {  
      return this.firstName + ' ' + this.lastName;  
    },  
    nameLength: function () {  
      return this.fullName.length;  
    }  
  }  
})

在这个例子中,fullName是一个计算属性,它依赖于firstName和lastName。而nameLength也是一个计算属性,它依赖于fullName。当firstName或lastName发生变化时,fullName将重新计算,并且由于nameLength依赖于fullName,因此它也会重新计算。这种依赖关系的传递使得计算属性能够响应式地处理数据变化。

通过以上案例,我们可以看到计算属性的缓存和依赖项在Vue.js中的重要性。这些特性使得计算属性在处理复杂逻辑和大量计算时能够提供高效和灵活的解决方案。

2 监视属性

2.1 引入监视属性

在 Vue.js 中,监视属性(Watched Properties)是通过使用 watch 选项来实现的。watch 选项使我们能够监视数据属性的变化,并在它们的值发生改变时执行某些操作。

下面是一个简单的 Vue.js 组件示例,展示了如何使用监视属性:

<template>  
  <div>  
    <input v-model="message" type="text" />  
    <p>{{ message }}</p>  
    <p>输入框被修改了: {{ modifiedCount }}</p>  
  </div>  
</template>  
  
<script>  
export default {  
  data() {  
    return {  
      message: '',  
      modifiedCount: 0,  
    };  
  },  
  watch: {  
    message(newVal, oldVal) {  
      this.modifiedCount++;  
    },  
  },  
};  
</script>

在这个例子中,我们定义了一个监视属性 message。当用户在输入框中输入文本时,message 属性的值会更新。在 watch 选项中,我们指定了当 message 属性发生变化时要执行的回调函数,即将 modifiedCount 值加 1。这样,每次输入框的值被修改时,modifiedCount 都会递增,显示了输入框被修改的次数。

  • watch 选项使我们能够监视数据属性的变化,并在它们的值发生改变时(回调函数自动调用)执行某些操作
  • 要使用监视属性,必须首先在组件的 data 选项中定义要监视的属性。然后,在 watch 选项中,我们可以指定要监视的属性以及对应的回调函数。当被监视的属性的值发生变化时,回调函数会被触发,并传入新旧属性值作为参数。

监视的两种写法:

  • 1)vm.$watch 是 Vue.js 中的一个方法,用于观察一个数据属性或者一个计算属性,当它们的值发生变化时,触发一个回调函数。

    下面是一个完整的案例,演示如何使用 vm.$watch 来观察一个数据属性并触发回调函数: vm.$watch 的用法如下:
    vm.$watch(expression, callback, [deep], [immediate])

    • expression:要观察的数据属性或者计算属性,可以是一个字符串或者一个函数。如果是字符串,表示要观察的属性名;如果是函数,表示要观察的计算属性,函数的返回值是要观察的值。
    • callback:当 expression 的值发生变化时触发的回调函数。回调函数的参数为新值和旧值。
    • deep:可选参数,表示是否深度观察对象。如果为 true,则对象的每个属性变化都会触发回调函数;如果为 false或者省略,则只有对象的引用变化才会触发回调函数。
    • immediate:可选参数,表示是否立即执行回调函数。如果为 true,则在观察开始时立即执行回调函数;如果为 false 或者省略,则只有在 expression 的值发生变化时才触发回调函数。

    在这里插入图片描述
    在这里插入图片描述
    在这个案例中,我们创建了一个 Vue 实例,并定义了一个数据属性 name 和一个计算属性 reversedName。在 mounted 钩子函数中,我们使用 this.$watch 方法来观察 name 属性的变化。当 name
    属性的值发生变化时,触发回调函数并将新值和旧值输出到控制台。

  • 2)new Vue时传入watch配置

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
    <h1>今天天气很 - {{ info }}</h1>
    <button @click="changeWeather()">切换天气</button>
</div>

<script type="text/javascript">
    new Vue({
        el: '#weather',
        data: {
            judgeHot: true,
        },
        computed: {
            info() {
                return this.judgeHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.judgeHot = !this.judgeHot
            }
        },
        watch: {
            judgeHot: {
                // immediate:true, //初始化时让hander()调用一下, 默认为false
                immediate: false,
                // handler什么时候调用?当judgeHot发生改变时
                handler(newValue, oldValue) {
                    console.log('judgeHot 被修改了 ...', newValue, oldValue)
                }
            },

            info: {
                handler(newValue, oldValue) {
                    console.log('info 被修改了 ...', newValue, oldValue)
                }
            }
        }
    })
</script>

</body>
</html>

在这里插入图片描述

2.2 监视属性的 immediate && deep

2.2.1 immediate

表示是否立即执行回调函数。如果设置为 true,则在初始化时立即执行回调函数,而不是等到属性值发生变化时才执行。

<template>  
  <div>  
    <input v-model="userInfo.name" placeholder="Name" />  
    <input v-model="userInfo.age" placeholder="Age" />  
    <p>Name: {{ userInfo.name }}</p>  
    <p>Age: {{ userInfo.age }}</p>  
  </div>  
</template>  
  
<script>  
export default {  
  data() {  
    return {  
      userInfo: {  
        name: '',  
        age: 0,  
      },  
    };  
  },  
  watch: {  
    userInfo: {  
      handler(newVal, oldVal) {  
        console.log('userInfo changed:', newVal, oldVal);  
      },  
      immediate: true, // 立即执行回调函数  
      deep: true, // 启用深度监视  
    },  
  },  
};  
</script>

在上面的示例中,我们将 immediate 属性设置为 true,这样在组件初始化时会立即执行回调函数,输出初始的 userInfo 对象。同时,我们也启用了深度监视,以便监视 userInfo 对象属性的变化。

2.2.2 deep

Vue.js 提供了深度监视(deep watching)的功能,用于监视对象和数组的变化。深度监视可以通过 watch 选项来实现,需要设置 deep 属性为 true。

下面是一个简单的示例,演示了如何使用深度监视来监视一个对象的变化:

<template>  
  <div>  
    <input v-model="userInfo.name" placeholder="Name" />  
    <input v-model="userInfo.age" placeholder="Age" />  
    <p>Name: {{ userInfo.name }}</p>  
    <p>Age: {{ userInfo.age }}</p>  
  </div>  
</template>  
  
<script>  
export default {  
  data() {  
    return {  
      userInfo: {  
        name: '',  
        age: 0,  
      },  
    };  
  },  
  watch: {  
    userInfo: {  
      handler(newVal, oldVal) {  
        console.log('userInfo changed:', newVal, oldVal);  
      },  
      deep: true, // 启用深度监视  
    },  
  },  
};  
</script>

在这个示例中,我们有一个 userInfo 对象,包含 name 和 age 两个属性。通过 v-model 指令,我们将输入框的值与 userInfo.name 和 userInfo.age 进行双向绑定。然后,我们使用 watch 选项来监视 userInfo 对象的变化,并设置 deep 属性为 true,以启用深度监视。当 userInfo 对象中的任何一个属性发生变化时,都会触发监视回调函数,并在控制台输出变化信息。

这样,无论是对 userInfo 对象中的哪个属性进行修改,都能够被深度监视捕获到,并在回调函数中处理相应的逻辑。

2.3 监视的简写形式

简写的前提是,如果不需要immediate、deep等的配置项,即配置项中只有handler的时候才可以简写。

<body>
<div id="root111">
    <h2>今天天气很{{ info }}</h2>
    <button @click="changeWeather">切换天气</button>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false

    const vm = new Vue({
        el: '#root111',
        data: {
            isHot: true
        },
        computed: {
            info() {
                return this.isHot ? '炎热' : '凉爽'
            }
        },
        methods: {
            changeWeather() {
                this.isHot = !this.isHot
            }
        },
        watch: {
            /*isHot:{
                handler(newValue,oldValue){
                    console.log('isHot被修改了',newValue,oldValue)
                }
            }*/

            isHot(newValue, oldValue) {
                console.log('isHot被修改了', newValue, oldValue)
            }
        }
    })

    vm.$watch('isHot', {
        handler(newValue, oldValue) {
            console.log('isHot 被修改了 ...', newValue, oldValue)
        }
    })
    //vm.$watch简写
    vm.$watch('isHot', function (newValue, oldValue) {
        console.log('isHot被修改了', newValue, oldValue)
    })
</script>
</body>
</html>

3 watch和computed在使用场景上的不同

两者都能实现的时候,选用computed比较简单,需要异步计算等比较复杂实现的时候用watch。computed和watch之间的区别:

  1. computed能完成的功能,watch都可以完成。
  2. watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
    两个重要的小原则:
  3. 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
  4. 所有不被Vue所管理的函数(定时器的回调函数、Ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
<body>
<div id="watch-name">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vm = new Vue({
        el: '#watch-name',
        data: {
            firstName: 'zhao',
            lastName: 'shuai-lc@inspur.com',
            fullName: 'zhao#shuai-lc@inspur.com'
        },
        watch: {
            firstName(newValue, oldValue) {
                this.fullName = newValue + '#' + this.lastName
            },
            lastName(newValue, oldValue) {
                this.fullName = this.firstName + '#' + newValue
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

<body>
<div id="watch-name">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:<span>{{ fullName }}</span>
</div>

<script type="text/javascript">
    Vue.config.productionTip = false
    let vm = new Vue({
        el: '#watch-name',
        data: {
            firstName: 'zhao',
            lastName: 'shuai-lc@inspur.com',
            fullName: 'zhao#shuai-lc@inspur.com'
        },
        watch: {
            firstName(val) {
                setTimeout(() => {
                    console.log(this) // vue
                    this.fullName = val + '-' + this.lastName
                }, 1000)

                setTimeout(function () {
                    console.log(this) // window
                    this.fullName = val + '-' + this.lastName
                }, 1000)

            },
            lastName(val) {
                this.fullName = this.firstName + '-' + val
            }
        }
    })
</script>
</body>
</html>

watch和computed在使用场景上的不同主要体现在以下两个方面:

  1. 响应式依赖:computed是响应式依赖的,即当一个数据受多个数据影响时,只有当依赖的数据发生变化时,关联的数据才会变化。因此,computed适合用于计算或格式化数据的场景。例如,在购物车应用中,我们可以使用computed来计算商品的总价,一旦购物车中的商品价格或数量发生变化,computed会自动重新计算总价。

  2. 我们可以使用watch来监听一个数据属性,一旦这个属性发生变化,就会触发一个异步操作。这个异步操作可以是发送一个请求到服务器,或者是进行一些其他的耗时操作。

下面是一个使用watch来监听数据属性,并在其变化时发送异步请求到服务器的示例:

new Vue({  
  el: '#app',  
  data: {  
    searchName: ''  
  },  
  watch: {  
    searchName(newVal, oldVal) {  
      axios.get(`/api/search?name=${newVal}`)  
        .then(response => {  
          console.log(response.data);  
        })  
        .catch(error => {  
          console.log(error);  
        });  
    }  
  }  
});
  1. 缓存性:computed的计算结果会被缓存起来,只有当依赖的数据发生变化时,才会重新计算。这样可以避免不必要的计算,提高性能。例如,如果有一个属性是由其他属性计算得到的,当这个属性依赖的其他属性发生改变时,computed会自动更新这个属性。而watch则不具备缓存性,它可以观察数据的变化并且触发回调。

综上所述,watch更适合用于需要监听数据变化并执行相应操作的场景,而computed更适合用于需要根据已有数据计算衍生数据的场景。根据具体的需求选择合适的选项可以使代码更加清晰、高效。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值