vue2之监听属性

849 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

上面那篇文章介绍了计算属性,那么我们这边文章我们就来介绍一下watch属性。

监听属性

监听属性也叫侦听属性,是可以直接监听数据的,即可以监听基本类型变量,也可以监听对象属性。

1. 基本用法

<input type="text" v-model="firstName" />
  watch: {
    firstName: {
      handler: function(newVal, oldVal) {
        console.log('firstName变化了,这是最新的值:', newVal);
      }
    }
  },

这时只要一输入值就能监听到firstName的变化

1670166129590.png

2. 即时监听

在有的应用场景中,我们需要页面从一开始的时候就开始监听变化,而不是只有在数据变化的时候才开始执行里面的handler函数,这时候就需要用到一个属性immediate。

  watch: {
    firstName: {
      handler: function(newVal, oldVal) {
        console.log('firstName变化了,这是最新的值:', newVal);
      },
      immediate: true
    }
   },

这时页面一挂载进来就能监听,执行handler函数

1670166350362.png

3. 深度监听

前面讲到的一些例子都是监听一些基本类型变量,很多时候我们需要监听到对象的变化,这时候就必须使用到另外一个属性deep。

<input type="text" v-model="person.name">
<input type="text" v-model="person.age">

data() {
  return {
      person: {
        name: '',
        age: 18
      }
  }
}

watch: {
    person: {
      handler: function(newVal) {
        console.log('person变化了');
      }
    },
    deep: true
},

上面这个例子只有加了deep属性才能监听到person的变化,否则是不能监听到,因为person的属性是个引用类型变量,person属性值存的实际上是一个地址,改变person对象的属性值对象本身是不变的,应用地址也不会改变,person就不会被监听到,只有加了deep属性才可以被监听到。

1670166911045.png

4. 简写形式

上面是watch的完整形式,如果只是想监听数据,而不需要使用deep属性和immediate属性的话,可以采用下面的简写属性。

// 完整形式
firstName: {
  handler: function(newVal, oldVal) {
    console.log('firstName变化了,这是最新的值:', newVal);
  }
}
// 简写形式
firstName(newVal, oldVal) {
  console.log('firstName变化了,这是最新的值:', newVal);
},

5. 原理

function initWatch (vm: Component, watch: Object) {
  // 1.遍历watch
  for (const key in watch) {
    const handler = watch[key]
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
    // 2.创建watcher
      createWatcher(vm, key, handler)
    }
  }
}
function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  //监听属性的值是一个对象,包含handler,deep,immediate
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  //如果回调函数是一个字符串,从VM中获取
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  //expOrFn是key, options是watch的全部选项
  return vm.$watch(expOrFn, handler, options)
}

6. watch可以使用异步函数

很多时候,监听数据变化后,我们需要进行一些异步操作,比如定时器回调:

firstName(newVal, oldVal) {
  setTimeout(() => {
      console.log('firstName变化了,这是最新的值:', newVal);
  })
},

7. 计算属性和监听属性的区别

上面一篇文章我们介绍过计算属性,还用了姓和名列举了全名的案例,这个案例也可以通过watch监听来实现。

<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
<div>{{fullName}}</div>

  data () {
    return {
      firstName: '',
      lastName: '',
      fullName: ''
    };
    },
    watch: {
    firstName(newVal, oldVal) {
      console.log('firstName变化了,这是最新的值:', newVal);
      this.fullName = newVal + '-' + this.lastName;
    },
    lastName(newVal, oldVal) {
      console.log('lastName变化了,这是最新的值:', newVal);
      this.fullName = this.firstName + '-' + newVal;
    }
  },

1670167615310.png

从上面这个例子中我们可以看出:

  1. computed能完成的功能,watch都可以完成
  2. watch能完成的功能,computed不一定能完成。例如: watch可以进行异步操作(setTimeout, ajax, promise)
  3. 所有能被vue管理的函数,最好写成普通函数,这样this才是vm或组件实例对象
  4. 所有不被vue管理的函数(定时器的回调函数,ajax的回调函数,promise回调函数),最好写成箭头函数,这样this才是vm或组件实例对象