前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue3 watch监听应用技巧

vue3 watch监听应用技巧

原创
作者头像
iwhao
发布2024-07-25 09:31:33
770
发布2024-07-25 09:31:33

之前用过vue2中的watch监听,最近在学vue3,对比两个版本对于watch使用的不同之处做个总结,然后记录下vue3中watch中的具体使用方法和技巧

watch在Vue2和Vue3中有哪些不同

语法不同

Vue 2 中,watch 选项接收一个对象,键是要观察的属性名,值是回调函数或者包含选项的对象。

Vue 3 中,watch 可以通过 watch 函数来实现,支持多种参数传递方式,更加灵活。

监听对象属性不同

Vue 2 中,直接监听对象的某个属性变化时,如果对象的属性被添加或删除,不会触发监听。

Vue 3 中,使用 watch 函数并结合 deep 选项可以更方便地监听对象属性的深层次变化,包括属性的添加和删除。

监听多个数据源

Vue 2 中,要监听多个数据源需要分别配置多个 watch 选项。

Vue 3 中,可以在一个 watch 函数中同时监听多个数据源。

清除副作用

Vue 3 中停止监听的函数 watch,方便在组件卸载等场景中清除副作用

watch的具体应用

在vue3中 文档中有说明 watch可监听的类型有四种:

  • 一个 ref(ref定义的数据)
  • 一个响应式对象(reactive定义的数据)
  • getter函数(函数返回一个值)
  • 一个包含上述内容的数组

watch函数一共有三个参数

第一个参数是监听的的源,源可为上方的四种类型

第二个参数是回调函数,返回新值和旧值和vue2一样,还有第三个参数onCleanup函数。onCleanup可以用来注册清理回调,在下次侦听器执行前会被调用。

第三个参数 是配置项(非必填可选择)包含一下配置

  • immediate: 值为true,会在侦听器创建时立即执行回调。
  • deep: 值为true 会深度监听对象内部的变化。
  • flush: 指定回调函数的执行时机
    • post (默认值): 侦听器回调会在 DOM 更新之后执行。
    • pre: 与post相反,表示侦听器回调会在 DOM更新之前执行 的更新。这个选项适用于需要在 DOM 更新之前访问旧 DOM 的场景。
    • sync: 表示侦听器回调会在数据变化时立即同步执行。这通常会导致更高的性能开销,因为它会阻止其他任务的执行,直到侦听器回调完成。这个选项适用于需要立即响应数据变化,并且变化不频繁的场景。
  • onCleanup: 一个在侦听器停止侦听之前执行的函数(可以用来清除无效的副作用,例如等待中的异步请求。)
  • onTrack: 在依赖项被追踪时触发
  • onTrigger: 在依赖项的值发生变化并触发更新时触发

监听ref

监听ref的普通类型

代码语言:javascript
复制
<template>
  <div>
    <h1>姓名:{{ man }}</h1>
    <button @click="changeName">改变姓名</button>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

let man = ref('帅哥')

function changeName(){
  man.value += '~真帅'
}
// 这里直接写man就可以,不要写man.vue
watch(man,(newValue,oldValue) => {
  console.log('改变了', newValue,oldValue)
})
</script>

停止watch的监视

watch函数返回一个用于停止监听的函数,执行这个返回函数就会停止watch的监视

代码语言:javascript
复制
<template>
  <div>
    <h1>计数:{{ num }}</h1>
    <button @click="changeName">累加</button>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

let num = ref(0)

function changeName(){
  num.value += 1
}

let stopWatch = watch(num,(newValue,oldValue) => {
  console.log('改变了', newValue,oldValue)
  // 当num 大于10的时候 停止监视
  if(newValue > 10){
    stopWatch()
  }
})
</script>

监听ref的对象类型

定义ref的对象类型,watch监听默认情况下只监听这个对象的本身(存储地址),而对象内部的元素值变化不会触发监听

代码语言:javascript
复制
<template>
  <div>
    <h1>姓名:{{ man.name }}</h1>
    <h1>年龄:{{ man.age }}</h1>
    <button @click="changeName">改变姓名</button>
    <button @click="changeAge">改变年龄</button>
    <button @click="changeMan">改变人</button>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

let man = ref({
  name: '帅哥',
  age: 20,
})

function changeName() {
  man.value.name += '~帅'
}
function changeAge() {
  man.value.age += 1
}
function changeMan() {
  man.value = {
    name: '美女',
    age: 18,
  }
}

watch(man, (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
})
</script>
<style>
button {
  margin: 10px;
}
</style>

如果也想要监听内部的元素值变化,可开启deep,但要注意的是,这时改变对象内部元素的时候 newValue, oldValue 是一样的都是新值,只有改变整个对象,才能拿到oldValue旧值

代码语言:javascript
复制
watch(man, (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
}, { deep: true})

未开启deep

开启deep

监听一个函数

如果只想监听对象中一个元素,例如只想监听name的变化,可以改为监听一个函数,函数返回这个对象的元素,但需要注意的是 这种写法改变整改对象(实例中的改变人方法)也会触发监听!

错误的写法,不能直接监听value,因为 它不属于watch可监听的四种类型,所以不支持

代码语言:javascript
复制
watch(man.value, (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
}, { deep: true })
代码语言:javascript
复制
// 默认只监听 man这个对象的地址,对象内部的元素值变化不会触发监听
watch(() => man.value.name, (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
}, { deep: true })

监听reactive

注意watch直接监听一个reactive是默认开启深度监听的,也就修改对象中任何一个值都会触发监听,且这种深度监听是不能关闭的。即便设置deep为false 也是无效的

当想修改整个reactive对象时,要注意,像以下这几种写法 都是错误的,这样会让man对象失去响应式

  • man={***}
  • man=reactive({***})
  • man.value={***}
  • man.value=reactive({***})

监听 reactive例子

代码语言:javascript
复制
<template>
  <div>
    <h1>姓名:{{ man.name }}</h1>
    <h1>年龄:{{ man.age }}</h1>
    <h1>我得第1部手机:{{ man.phones.phone1 }}</h1>
    <h1>我得第2部手机:{{ man.phones.phone2 }}</h1>
    <button @click="changeName">改变姓名</button>
    <button @click="changeAge">改变年龄</button>
    <button @click="changePhone1">改变第一部手机</button>
    <button @click="changePhone2">改变第二部手机</button>
    <button @click="changePhone">改变所有手机</button>
    <button @click="changeMan">改变这个人</button>
  </div>
</template>

<script setup>
import { reactive, watch } from 'vue'

let man = reactive({
  name: '帅哥',
  age: 20,
  phones: {
    phone1: '红米',
    phone2: '苹果',
  }
})

function changeName() {
  man.name += '~帅'
}
function changeAge() {
  man.age += 1
}
function changePhone1() {
  man.phones.phone1 = '华为'
}
function changePhone2() {
  man.phones.phone2 = '小米'
}
function changePhone() {
  man.phones = {
    phone1: 'vivo',
    phone2: 'oppo',
  }
}
function changeMan() {
  // // 错误的写法 1  数据不生效,页面不相应,watch也不会触发
  // man = reactive({
  //   name: '大神',
  //   age: 30,
  //   phones: {
  //     phone1: '诺基亚1',
  //     phone2: '诺基亚2',
  //   }
  // })
  // // 错误的写法 2   数据不生效,页面不相应,watch也不会触发
  // man = {
  //   name: '大神',
  //   age: 30,
  //   phones: {
  //     phone1: '诺基亚1',
  //     phone2: '诺基亚2',
  //   }
  // }
  // // 错误的写法3 数据不生效,页面不相应,watch会触发,
  // man.value = {
  //   name: '大神',
  //   age: 30,
  //   phones: {
  //     phone1: '诺基亚1',
  //     phone2: '诺基亚2',
  //   }
  // }
  // 正确的写法 watch响应 回调里的新旧值一样
  Object.assign(man, {
    name: '大神',
    age: 30,
    phones: {
      phone1: '诺基亚1',
      phone2: '诺基亚2',
    }
  })
}

watch(man, (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
})
</script>
<style>
button {
  margin: 10px;
}
</style>
</script>

第一种情况

如果想要只监听man对象中的某一个元素,方法的话和上文中监听ref的方法一样,

把监听源换成一个函数,函数返回这个reactive对象的属性

错误的写法

代码语言:javascript
复制
watch(man.age, (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
})

正确的写法

代码语言:javascript
复制
watch(() => man.age, (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
})

第二种情况

如果想要只监听man对象中的多个元素,例如只想监听man的age和 phones 下的phone1

监视源就要改为一个数组,数据的元素为函数, 要注意的是此时的回调函数里返回的 newValue、oldVaule 也就只剩下监视的这两个值了

代码语言:javascript
复制
watch([() => man.age, () => man.phones.phone1], (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
})

第三种

只监听 age和 phones,这时候 phones 就不用函数了

代码语言:javascript
复制
watch([() => man.age, man.phones], (newValue, oldValue) => {
  console.log('改变了', newValue, oldValue)
})

总结一句话就是,如果要监听对象里的某个属性,那监听资源就要写函数式

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • watch在Vue2和Vue3中有哪些不同
    • 语法不同
      • 监听对象属性不同
        • 监听多个数据源
          • 清除副作用
          • watch的具体应用
            • 监听ref
              • 监听ref的普通类型
            • 停止watch的监视
              • 监听ref的对象类型
            • 监听一个函数
              • 监听reactive
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档