上一节我们简单的介绍了一下 vue3 项目中的计算属性,这一节我们继续 vue3 的基础知识讲解。

这一节我们来说 vue3 的侦听器。

学过 vue2 的小伙伴们肯定学习过侦听器,主要是用来监听页面数据或者是路由的变化,来执行相应的操作,在 vue3里面呢,也有侦听器的用法,功能基本一样,换汤不换药的东西。 侦听器是常用的 Vue API 之一,它用于监听一个数据并在数据变动时做一些自定义逻辑,本文将先列举侦听器在 Vue 中的使用方式,然后再分析源码讲述为什么可以这样使用、以及侦听器的实现原理。下面我们稍微说一下侦听器。

watch 侦听器使用。

watch API 使用,至少需要指定两个参数: source 和 callback,其中 callback 被明确指定只能为函数,所以不同是用方式的差别其实只在 source 。

接下来我们看一下 vue3 侦听器的基本使用:

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num" />
    <br>
    <br>
    <el-button type="primary" @click="num++">num + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref } from 'vue'
  export default {
    setup() {
      const num = ref(1)
      watch(num, (newVal, oldVal) => {
        console.log("新值:", newVal, "   旧值:", oldVal)
      })
      return { num, }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

上面的代码是页面上有一个数字,点击按钮一下,数字加一,然后侦听器侦听数字的变化,打印出侦听的最新值和老值。

OK。上边的案例就是 vue3 侦听器的简单案例,侦听器和计算属性一样,可以创建多个侦听器,这个是没有问题的,案例就不写了,和上一节讲的声明多个计算属性是一致的。如果有不明白的可以看一下我的上一篇博客。

上边我们说过这么一句话,watch API 至少需要指定两个参数: source 和 callback。通过上边的案例我们看到了, 确实是两个,source 是监听的数据,callback 是监听回调,那为啥说是至少呢?

对的,因为他还有第三个参数 —— 配置对象。

在 vue2 里面,我们打开页面就像让侦听器立即执行,而不是在第一次数据改变的时候才开始执行,这时候有一个参数叫 immediate ,设置了这个参数,创建第一次就执行,所以说呢,vue3 同样可以使用。

上面的案例刷新执行的时候发现,在点击按钮之前,也就是 num 创建的时候,侦听器是没有执行的,所以说呢,加上 immediate 参数,就可以让侦听器立即执行操作。

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num" />
    <br>
    <br>
    <el-button type="primary" @click="num++">num + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref } from 'vue'
  export default {
    setup() {
      const num = ref(1)
      watch(num, (newVal, oldVal) => {
        console.log("新值:", newVal, "   旧值:", oldVal)
      }, {
        immediate: true
      })
      return { num, }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

我们看到,刷新完页面,还没有点击按钮让 num 加一的,控制台就有数据打印了,为什么呢?就是因为我们加了 immediate 为 true,让侦听器立即执行。控制台输出最新的值也就是我们初始化的值1,老的值没有,所以输出了 undefined。

侦听器监听 reactive

上面说了侦听器侦听单个数据,他也可以用来侦听对象的变化。

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num.age" />
    <br>
    <br>
    <el-button type="primary" @click="num.age++">num + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref, reactive } from 'vue'
  export default {
    setup() {
      const num = reactive({
        name: '我是𝒆𝒅.',
        age: 10
      })
      watch(num, (newVal, oldVal) => {
        console.log(newVal, oldVal)
      })
      return { num }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

比如说上面代码,我们侦听 num 这个对象的变化。

看效果我们发下,在监听整个 reactive 响应式对象的时候,确实当里面的属性值发生改变了之后可以被侦听器检测到,但是 newVal 和 oldVal 的值都是新的,默认是10,点击之后,新值是 11 很正常,但是老值不应该是 10 吗?为什么这里老值和新值一样也是 11 呢?

这个不需要疑问哈,如果监听整个 reactive 数据的话,只能回调到最新的值,获取不到老的值。

那问题来喽,我就修改 age 属性,我就要获取 age 老的值怎么办?其实我们只需要监听 num 下面的 age 就可以了,先看下面的代码哈。

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num.age" />
    <br>
    <br>
    <el-button type="primary" @click="num.age++">num + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref, reactive } from 'vue'
  export default {
    setup() {
      const num = reactive({
        name: '我是𝒆𝒅.',
        age: 10
      })
      watch(num.age, (newVal, oldVal) => {
        console.log(newVal, oldVal)
      })
      return { num }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

我们监听对象直接是 num.age, 监听年龄属性值,保存看一下效果。

刷新结果我们可以看到哈,我们啥都没干,侦听器直接报了一个警告给我们,啥意思呢,其实不能直接这样监听。

当我们需要监听某个对象属性的时候,我们不能直接对象点属性的方式进行监听,需要传入一个 getter 方法,也就是箭头函数进行监听,下面的代码是正确方式哈。

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num.age" />
    <br>
    <br>
    <el-button type="primary" @click="num.age++">num + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref, reactive } from 'vue'
  export default {
    setup() {
      const num = reactive({
        name: '我是𝒆𝒅.',
        age: 10
      })
      watch(() => num.age, (newVal, oldVal) => {
        console.log(newVal, oldVal)
      })
      return { num }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

OK,保存刷新,我们发现,侦听器已经不报错了,而且我们点击按钮让 age 加一的时候,可以顺利的监听到 age 的变化,并且回调出最新值和上一次的值。

通过箭头函数,我们就可以实现对象属性的监听。

很多人说,vue2 在监听对象的时候需要对侦听器设置深度侦听,为什么 vue3 这个不需要呢?因为他监听响应式对象,默认就是深度监听。但是,如果监听的是深度嵌套对象或数组中的 property 变化时,仍然需要 deep 选项设置为 true。

看下面的案例,我们监听深层嵌套的 time 属性值。其实我觉得没大必要,不使用箭头函数其实可以。但是还是写一下吧。

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num.todo.time" />
    <br>
    <br>
    <el-button type="primary" @click="num.todo.time ++">num.todo.time + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref, reactive, computed, } from 'vue'
  export default {
    setup() {
      const num = reactive({
        name: '我是𝒆𝒅.',
        age: 10,
        todo: {
          name: '弹吉他',
          time: 1
        }
      })
      watch(() => num, (newVal, oldVal) => {
        console.log(newVal.todo.time, oldVal.todo.time)
      })
      return { num }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

保存代码刷新,发现点击之后没有监听到。

这个时候就可以加上 deep 深度监听。

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num.todo.time" />
    <br>
    <br>
    <el-button type="primary" @click="num.todo.time ++">num.todo.time + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref, reactive, computed, } from 'vue'
  export default {
    setup() {
      const num = reactive({
        name: '我是𝒆𝒅.',
        age: 10,
        todo: {
          name: '弹吉他',
          time: 1
        }
      })
      watch(() => num, (newVal, oldVal) => {
        console.log(newVal.todo.time, oldVal.todo.time)
      }, { deep: true })
      return { num }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

加上深度监听 { deep:true }

我们可以看到打印出信息来了,其实我觉得这个方法有点多余,但是万一用到呢是吧?啊哈哈哈哈,自己根据情况选择使用吧。

但是有一点要注意哈!深度侦听需要遍历被侦听对象中的所有嵌套的 属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。

监听多个参数执行各自逻辑

本来不打算说了,但是逼逼赖赖这么久了,稍微简单提一下吧。

比如说我们需要监听多个参数,假设两个哈,然后每个参数监听到之后,执行的逻辑是不一样的,我们可以创建多个侦听器来分别监听,不写全部代码了,只写关键代码了哈。

      // 第一个
      watch(num, (newVal, oldVal) => {
        console.log(newVal, oldVal)
      })

      // 第二个
      watch(()=> boy.age, (newVal, oldVal) => {
        console.log(newVal, oldVal)
      })

监听多个参数执行相同逻辑

这个的意思就是无论是 num 改变还是 boy.age 改变,我执行的代码都是一样的。看下面案例:

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num.name" />
    <el-input v-model="num.age" />
    <br>
    <br>
    <el-button type="primary" @click="num.todo.time ++">num.todo.time + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref, reactive, computed, } from 'vue'
  export default {
    setup() {
      const num = reactive({
        name: '我是𝒆𝒅.',
        age: 10,
        todo: {
          name: '弹吉他',
          time: 1
        }
      })

      watch([() => num.name, () => num.age], (newVal, oldVal) => {
        console.log(newVal, oldVal)
      })
      return { num }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

保存刷新页面,修改 name 和 age 的值。

上面我们把数据源以数组的方式传入,返回的回调参数,新值和旧值都是以数组的方式返回,新值旧值的数组内顺序就是我们数据源传入的顺序,都能看出来哈。

如果你不想让他返回数组你可以这样改一下,其实都差不多,了解一下,根据实际情况选择性使用就行。

<template>
  <div>
    <h1>watch 侦听器</h1>
    <el-input v-model="num.name" />
    <el-input v-model="num.age" />
    <br>
    <br>
    <el-button type="primary" @click="num.todo.time ++">num.todo.time + 1</el-button>
  </div>
</template>
<script>
  import { watch, ref, reactive, computed, } from 'vue'
  export default {
    setup() {
      const num = reactive({
        name: '我是𝒆𝒅.',
        age: 10,
        todo: {
          name: '弹吉他',
          time: 1
        }
      })

      watch([() => num.name, () => num.age], ([newName, newAge], [oldName, oldAge]) => {
        console.log(newName, newAge, oldName, oldAge)
      })
      return { num }
    }
  }
</script>
<style scoped>
  .el-input {
    width: 100px;
  }
</style>

保存刷新查看一下效果。

以上就是详解Vue3中侦听器watch的使用教程的详细内容,更多关于Vue3侦听器watch的资料请关注阿兔在线工具其它相关文章!

点赞(0)

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部