Vue3快速上手(十)数据变化监视之侦听器watch和watchEffect

本文详细介绍了Vue中如何使用watch、reactive和watchEffect来监视不同类型的数据源变化,包括ref定义的数据、响应式对象、getter函数返回值和数组。重点讲解了它们的使用场景、区别和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

一、watch介绍

侦听器watch是用来监视数据变化,并进行一些附加操作的。
watch可以侦听数据源类型​有以下4种:

  1. 一个 ref定义的数据 (包括计算属性)
  2. 一个响应式对象数据(ref类,reactive类)
  3. 一个 getter 函数(有1个返回值的函数)
  4. 多个数据源组成的数组

二、监视ref定义的数据

监视ref定义的基本类型的数据,实际是观察.value的变化。

 <template>
    <div class="person">
        <h2>watch-ref</h2>
        <h3>A<input type="text" v-model="a" /></h3>
        <h2>{{ a }}</h2>
    </div>
</template>
<script lang='ts' setup name="Computed">
import { computed, ref, watch } from 'vue'
let a = ref(1)

// 监视sum的变化, 返回值是一个停止监听函数
let stopWatch = watch(a, (newVal, oldVal) => {
    if(newVal >= 100){
        stopWatch()
    }
    console.log('newVal: ', newVal);
    console.log('oldVal: ', oldVal);
})
</script>

页面效果如下:
在这里插入图片描述
从图上可以看出:
1、成功监听了a.value的变化
2、监听返回一个函数,是停止函数,可以在特定条件下,调用其来停止监听。stopWatch的输出如下图,其功能性显而易见。
在这里插入图片描述

三、监视ref定义的响应式对象类型数据

3.1 监听对象整体的变化

监听对象时,监听的是对象地址的变化,如果只是对象属性值的变化,则不会被监听。

<template>
    <div class="person">
        <h2>watch-ref-obj</h2>
        <h3>姓名:{{ person.name }}</h3>
        <h3>年龄:{{ person.age }}</h3>
        <button @click="updatePersonAge">修改age</button>
        <button @click="updatePerson">修改整个Person</button>
    </div>
</template>
<script lang='ts' setup name="Computed">
import {  ref, watch } from 'vue'
let person = ref(
    {
        name: "李四",
        age: 99
    }
)

function updatePersonAge() {
    person.value.age += 1
}

function updatePerson() {
    person.value = {name: '王五', age: 100}
}

// 监视person的变化, 返回值是一个停止监听函数,监听的是对象地址的变化,如果只是属性值变化,则不会被监听。
let stopWatch = watch(person, (newVal, oldVal) => {
    console.log('newVal: ', newVal);
    console.log('oldVal: ', oldVal);
})
</script>

页面效果如下:

在这里插入图片描述
从图上可以看出:
1、修改单个属性值的时候,是没有被监听的
2、修改整个对象时,是监听了的。因为对象地址发生了变化。

3.2 监听对象内部属性值的变化

// 如果要监视对象内部属性值的变化,则需要配置deep为true,当然这种设置在地址值发生变化的时候也是会监听的。
// 但只改对象属性的时候,新旧值是一样的,都是新值

let stopWatch = watch(person, (newVal, oldVal) => {
    console.log('newVal: ', newVal);
    console.log('oldVal: ', oldVal);
}, {deep: true})

页面效果如下:
在这里插入图片描述
★ 从上述动图可以看到,只是改age的时候,新旧的值是完全一样的,且都是新值,这是为什么呢?是因为这里改的是对象里属性的值,并没有改对象本身的地址。

四、监视reactive定义的响应式对象类型数据

监听reactive的响应式对象时,默认是开启{deep: true}的。且设置为false的时候是不生效的。

<template>
    <div class="person">
        <h2>watch-ref-obj</h2>
        <h3>姓名:{{ person.name }}</h3>
        <h3>年龄:{{ person.age }}</h3>
        <button @click="updatePersonAge">修改age</button>
        <button @click="updatePerson">修改整个Person</button>
    </div>
</template>
<script lang='ts' setup name="Computed">
import {  reactive,  watch } from 'vue'
let person = reactive(
    {
        name: "李四",
        age: 99
    }
)

function updatePersonAge() {
    person.age += 1
}

function updatePerson() {
    Object.assign(person, {name: '王五', age: 100})
}

// 监视person的变化, 监听reactive的响应式对象时,默认是开启`{deep: true}`的。
// let stopWatch = watch(person, (newVal, oldVal) => {
//     console.log('newVal: ', newVal);
//     console.log('oldVal: ', oldVal);
// })
// 配置deep为true和不配置是一样的效果
let stopWatch = watch(person, (newVal, oldVal) => {
    console.log('newVal: ', newVal);
    console.log('oldVal: ', oldVal);
}, {deep: true})
</script>

页面效果如下:
在这里插入图片描述
★ 从上述动图可以看到,改age的时候,新旧的值是完全一样的,且都是新值,这是为什么呢?是因为这里改的是对象里属性的值,并没有改对象本身的地址。
★ 从上述动图可以看到,改整个对象的时候,新旧的值是完全一样的,且都是新值,这是为什么呢?是因为这里Object.assign()方法改的也是对象里属性的值类似于一个一个set值,也没有改对象本身的地址。

五、监视一个 getter 函数(有1个返回值的函数)

监听对象为() => {return person.bmi}是一个有返回值的函数

<template>
    <div class="person">
        <h2>watch-getter</h2>
        <h3>姓名:{{ person.name }}</h3>
        <h3>年龄:{{ person.age }}</h3>
        <h3>身高:{{ person.bmi.high }}</h3>
        <h3>体重:{{ person.bmi.weight }}</h3>
        <button @click="updatePersonName">修改name</button>
        <button @click="updatePersonAge">修改age</button>
        <button @click="updateBmiHigh">修改身高</button>
        <button @click="updateBmi">修改bmi</button>
    </div>
</template>
<script lang='ts' setup name="Computed">
import {  reactive,  watch } from 'vue'
let person = reactive(
    {
        name: "李四",
        age: 99,
        bmi:{
            high: 173,
            weight: 82
        }
    }
)

function updatePersonName() {
    person.name += '@'
}
function updatePersonAge() {
    person.age += 1
}

function updateBmiHigh() {
    person.bmi.high += 1
}

function updateBmi() {
    person.bmi = {high: 190, weight: 70}
}

// 只监听对象的一个属性,该属性是基础类型
watch(() => {return person.age}, (newVal, oldVal) => {
    console.log('newVal: ', newVal);
    console.log('oldVal: ', oldVal);
})

// 只监听对象的一个属性,但该属性是对象类型
let stopWatch = watch(() => {return person.bmi}, (newVal, oldVal) => {
    console.log('newVal: ', newVal);
    console.log('oldVal: ', oldVal);
}, {deep:true})
</script>

页面效果如下:
在这里插入图片描述
由上图可以看出:
1、单独监听age的时候,修改name是没有输出的;
2、监听bmi的时候 ,单独修改身高,和修改整个bmi都是有被监听,有输出的

此外,如果监听的是对象的属性A是对象类型,那么如果需要在修改整个对象A和修改对象A下的某个属性都监听变化的话,这需要加上{deep:true}的配置才行。

六、监视多个数据源组成的数组

示例:监听人的age和身高

<template>
    <div class="person">
        <h2>watch-arr[响应式数据列表]</h2>
        <h3>姓名:{{ person.name }}</h3>
        <h3>年龄:{{ person.age }}</h3>
        <h3>身高:{{ person.bmi.high }}</h3>
        <h3>体重:{{ person.bmi.weight }}</h3>
        <button @click="updatePersonName">修改name</button>
        <button @click="updatePersonAge">修改age</button>
        <button @click="updateBmiHigh">修改身高</button>
        <button @click="updateBmi">修改bmi</button>
    </div>
</template>
<script lang='ts' setup name="Computed">
import {  reactive,  watch } from 'vue'
let person = reactive(
    {
        name: "李四",
        age: 99,
        bmi:{
            high: 173,
            weight: 82
        }
    }
)

function updatePersonName() {
    person.name += '@'
}
function updatePersonAge() {
    person.age += 1
}

function updateBmiHigh() {
    person.bmi.high += 1
}

function updateBmi() {
    person.bmi = {high: 190, weight: 70}
}

// 监视多个数据源组成的数组 [() => {return person.age}, () => {return person.bmi.high}]
let stopWatch = watch([() => {return person.age}, () => {return person.bmi.high}], (newVal, oldVal) => {
    console.log('newVal: ', newVal);
    console.log('oldVal: ', oldVal);
})
</script>

页面效果如下:
在这里插入图片描述
由上图可以看到:
1、成功的监听了age和high
2、newVal和oldVal都是数组形式,索引顺序和监听顺序保持一致。

七、watchEffect以及和watch的区别

7.1 watchEffect

使用watchEffect监听数据,会立即运行一个函数,同时监听响应式数据,并在数据更改时重新执行该函数。
示例如下:

<template>
    <div class="person">
        <h2>watch-watchEffect</h2>
        <h3>姓名:{{ person.name }}</h3>
        <h3>年龄:{{ person.age }}</h3>
        <h3>身高:{{ person.bmi.high }}</h3>
        <h3>体重:{{ person.bmi.weight }}</h3>
        <button @click="updatePersonAge">修改age</button>
        <button @click="updateBmiHigh">修改身高</button>
    </div>
</template>
<script lang='ts' setup name="Computed">
import {  reactive,  watch, watchEffect } from 'vue'
let person = reactive(
    {
        name: "李四",
        age: 99,
        bmi:{
            high: 173,
            weight: 82
        }
    }
)
function updatePersonAge() {
    person.age += 1
}
function updateBmiHigh() {
    person.bmi.high += 1
}

// watchEffect实现
watchEffect(()=>{
    // 身高大于178,或者,年龄大于102的时候提示
    if(person.age > 102 || person.bmi.high > 178){
        console.log('身高大于178,或者,年龄大于102了');
        // to do something
    }
})

// watch实现
watch([() => person.age, () => person.bmi.high], (newVal) => {
    // 身高大于178,或者,年龄大于102的时候提示
    let [newAge, newHigh] = newVal
    if (newAge > 102 || newHigh > 178) {
        console.log('身高大于178,或者,年龄大于102了');
        // to do something
    }
})
</script>

页面效果如下:
在这里插入图片描述
从上述示例中 watchEffect实现 watch实现实现同一个功能的代码中,就可以看到二者的区别。从而根据实际场景进行选择使用。

7.2 watchEffect和watch的对比

项目watchwatchEffect
是否具有监听数据变化的功能
是否需要指明监听对象是(指哪儿打哪儿)否(打哪儿指哪儿)

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一掬净土

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值