Vue的 computed 和 watch

123 阅读2分钟

计算属性Computed

定义

当其依赖的属性的值发生变化时,计算属性会重新计算,反之,则使用缓存中的属性值

为什么使用计算属性

模板内的放入过多复杂逻辑的东西。会让模板变得复杂,且难于维护。

使用方法

  const vm = new Vue({
            el:"#app",
            data:{
                user:{
                    name:"AAA",
                    email:"12345"
                }
            },
            computed:{
                displayName:{
                    get(){
                        const user = this.user 
                        return user.name || user.email
                    },
                    set(value){
                        this.user.name = value
                    }
                }
            },
            template:`
            <div>
               {{displayName}}
               <button @click="changeName">变</button>    
            </div>
            `,
            methods:{
                changeName(){
                    this.user.name = "BBB"
                }
            }
        })

如代码所示,计算属性以对象形式编写,不加括号,可以设置getter和setter

计算属性缓存 vs 方法

计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要数据不发生改变,多次访问计算属性会立即返回之前的计算结果,而不必再次执行函数,节省内存。

简而言之:

  1. 如果依赖的属性不变化,计算属性就不会重新计算
  2. getter、setter 默认不做缓存

watch

当 data 的数据发生变化时,执行对应的回调函数,有两个参数,一个 value (修改后的 data 数据),一个 oldValue(原来的 data 数据)

代码示例

new Vue({
  data: {
    n: 0,
    obj: {
      a: "a"
    }
  },
  template: `
    <div>
      <button @click="n += 1">n+1</button>
      <button @click="obj.a += 'hi'">obj.a + 'hi'</button>
      <button @click="obj = {a:'a'}">obj = 新对象</button>
    </div>
  `,
  watch: {
    n() {
      console.log("n 变了");
    },
    obj:{
      handler: function (val, oldVal) { 
      console.log("obj 变了")
    },
      deep: true // 该属性设定在任何被侦听的对象的 property 改变时都要执行 handler 的回调,不论其被嵌套多深
    },
    "obj.a":{
      handler: function (val, oldVal) { 
      console.log("obj.a 变了")
    },
      immediate: true // 该属性设定该回调将会在侦听开始之后被立即调用
    }
  }
}).$mount("#app");

如何定义变化

参考=== 的计算规则,简单类型看值,复杂类型看地址

语法

  • 一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个 property。

  • 示例

    var vm = new Vue({
      data: {
        a: 1,
        b: 2,
        c: 3,
        d: 4,
        e: {
          f: {
            g: 5
          }
        }
      },
      watch: {
        a: function (val, oldVal) {
          console.log('new: %s, old: %s', val, oldVal)
        },
        // 方法名
        b: 'someMethod',
        // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
        c: {
          handler: function (val, oldVal) { /* ... */ },
          deep: true
        },
        // 该回调将会在侦听开始之后被立即调用
        d: {
          handler: 'someMethod',
          immediate: true
        },
        // 你可以传入回调数组,它们会被逐一调用
        e: [
          'handle1',
          function handle2 (val, oldVal) { /* ... */ },
          {
            handler: function handle3 (val, oldVal) { /* ... */ },
            /* ... */
          }
        ],
        // watch vm.e.f's value: {g: 5}
        'e.f': function (val, oldVal) { /* ... */ }
      }
    })
    vm.a = 2 // => new: 2, old: 1
    
  • deep 控制是否要看这个对象里面的属性变化

  • immediate 控制是否在第一次渲染是执行这个函数

  • 不应该使用箭头函数来定义 watcher 函数,因为箭头函数没有 this,它的 this 会继承它的父级函数,但是它的父级函数是 window,导致箭头函数的 this 指向 window,而不是 Vue 实例

另一种调用方式

  • vm.$watch('data属性名', fn, {deep: .., immediate: ..})

总结

  • 如果一个数据需要复杂计算并多次调用就使用计算属性
  • 如果一个数据的变化需要被监听并且对数据做一些操作就用 watch