Vue 组件中5种Watch方式的使用
解释

watch是一个对象,这个对象的键是需要观察的表达式,值可以是直接方法、方法名、包含选项的对象等等,Vue实例会在实例化时调用$watch(), 遍历watch对象的每一个属性,并且观察。

注意,本文不会阐述watch对象的具体实现源码,只是介绍watch对象键值的五种使用方式。


格式:
watch {
    { [key: string]: string | Function | Object | Array }
}

使用方式:
  1. 最直接的,直接用方法作为回调函数(或者说作为值)。

    // 我们用vue的v-model指令来验证下watch对象的功能
    <div>
      <p>{{msg}}</p>
      <input v-model="msg">
    </div>
    // pattern1 directly method value
    watch {
        // msg1 为表达式, function(oldVal, newVal)为匿名函数,作为回调函数
        msg1: function (newVal, oldVal) {
            console.log("oldVal is: " + oldVal + " newVal is: " + newVal);
        }
    }

    在双向绑定输入框输入一些值后,看console里watch对象的效果:

    // 初始值为init,输入init1之后的console输出
    oldVal is: init newVal is: init1

    注意,回调函数的第一个参数是新的值,第二个是旧的值。

  2. 函数名作为值

    // 同样用v-model验证效果
    <div>
      <p>{{msg2}}</p>
      <input v-model="msg2">
    </div>
    // pattern 2 use function name as value
    watch {
        msg2: "methodway"
    }
    
    methods: {
        methodway (newVal, oldVal) {
          console.log("oldVal is: " + oldVal + " newVal is: " + newVal);
        }
    }

    console输出:

    // 初始值为init,输入init1之后的console输出
    oldVal is: init newVal is: init1
  3. 观察键为对象/数组时,需要追加选项deep

    当我们的被观察者是一个对象/数组的时候,我们改变了对象内部属性的值,用通常的watch方式是捕捉不到的。因为对于数组、对象本身来说,它并没有改变。这个时候需要加上选项deep: true

    <div>
      <p>{{msg3.attr1}}</p>
      <input v-model="msg3.attr1">
    </div>

    如果使用通常方式,改变了msg3的attr1属性值,watch并不会响应。需要加上deep选项:

    watch {
        msg3: {
          handler: function (newVal, oldVal) {
              console.log("oldVal is: " + oldVal + " newVal is: " + newVal);
          },
          // 加上这个选项之后,不论被嵌套得多深,被观察的对象的property一旦改变都会响应
          deep:true
        }
    }

    但是在这里的一个输出会有一个问题,虽然你能捕捉到对象的变更,但是你会发现oldVal和newVal是一样的,console输出:

    // 对象的回调函数种,oldVal和newVal是一致的
    oldVal is: {"attr1":"123","attr2":"2"} newVal is: {"attr1":"123","attr2":"2"}

    也就是说,虽然我们能捕捉到变化,但是从watch对象里面的回调来说,我们无法准确抓取到变化的属性。如果想做到这点,我们可以单独观察对象的某个属性msg3.attr1或者使用computed做中间层来观察。

    // computed做中间层,注意,并不是非要这样多写一层进行观察,可以直接观察属性
    computed: {
      msg3attr() {
        return this.msg3.attr1;
      }
    }
    
    watch {
        msg3attr: 
          function(newVal, oldVal) {
              console.log("oldVal is: " + oldVal + " newVal is: " + newVal);
          }
    }

    这样就能观察到属性值的变化:

    oldVal is: "12" newVal is: "123"
  4. 实例化后立刻回调

    我们有时候会有要求Vue对象实例化后,立刻回调某一个Watch对象的键值。这个时候只需要加上一个选项immediate: true即可

    <div>
      <p>{{msg4}}</p>
      <input v-model="msg4">
    </div>
    watch {
      msg4: {
        handler(newVal, oldVal) {
          console.log("this should be called immediately.");
        },
        // 加上immediate选项后,实例化后立刻回调一次
        immediate:true
      },
    }

    console输出:

    // 注意,我们并没有改变input的值,实例化之后立刻进行了回调
    this should be called immediately.
  5. 回调函数数组

    如果我们有多个回调函数需要执行,我们可以将Watch的值赋值为一个数组,数组内的函数将被逐一调用。

    <div>
      <p>{{msg5}}</p>
      <input v-model="msg5">
    </div>
    watch {
      msg5: [
        "methodchain1",
        function methodchain2(oldVal,newVal) {
          console.log("second method");
        },
        {
          handler(newVal, oldVal) {
          console.log("third method")
        },deep:true
        }
      ]
    },
    
    methods: {
      methodchain1(newVal, oldVal) {
        console.log("first method");
      }
    }

    当msg5变更之后,console的输出:

    // 回调函数被逐一调用
    first method
    second method
    third method
  • 值得一提的是,watch对象中,有时候键可以省略,我们用键的名字直接用作回调函数的名称,同样能正常观察到表达式的变化。

    <div>
      <p>{{msg}}</p>
      <input v-model="msg">
    </div>
    watch {
      // 直接用表达式作为回调函数名称,效果一致
      msg(newVal, oldVal) {
        console.log("oldVal is: " + oldVal + " newVal is: " + newVal);
      }
    }

    console输出:

    oldVal is: init newVal is: 1

需要特别注意的是,Watch对象中的回调函数不应该用箭头函数进行定义,它将导致上下文的混乱,this将指向错误的上下文。

以上为回调函数在组件中的五种使用方法,欢迎点赞、评论、收藏。


melancholy
17 声望1 粉丝

30days: front-end to full-stack