监视 ref()
定义的【基本类型】数据
语法: watch(变量名, (newValue, oldValue) => {})
监视时直接写变量名,其本质上监视的是 .value
。
使用 watch()
监视 count
这个响应式变量的变化。当 count
的值发生变化时,回调函数会被执行,打印出旧值和新值。
<template>
<div>
<div>count: {{ count }}</div>
<button @click="addCount">点击 count+1</button>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
let count = ref(0)
const addCount = () => {
count.value++
}
// watch 监视的是 ref定义的数据:count
watch(count, (newValue, oldValue) => {
console.log(`count 从 ${oldValue} 变为 ${newValue}`);
})
</script>
监视 ref()
定义的【对象类型】数据
语法: watch(变量名, (newValue, oldValue) => {})
监视时直接写变量名,监视的是对象的引用地址值,如果想监视对象的内部属性变化,要手动开启深度监视(deep: true
)。
- 如果修改的是
ref()
定义的对象中的属性,newValue
和oldValue
都是新值,因为它们是同一个对象(同一个引用地址)。 - 如果修改整个
ref()
定义的对象,newValue
是新值,oldValue
是旧值,因为不是同一个对象了。
<template>
<div>
<p>姓名: {{ person.name }}</p>
<p>年龄: {{ person.age }}</p>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changePerson">修改整个人</button>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
const person = ref({
name: '张三',
age: 18
});
const changeName = () => {
person.value.name += '哈'
}
const changeAge = () => {
person.value.age ++
}
const changePerson = () => {
person.value = { name: '李四', age: 17 }
}
// changeName、changeAge修改person的内部属性,不会改变person的引用地址,不会触发 watch
// changePerson 对 person 重新赋值,改变了person的引用地址,触发watch
watch(person, (newValue, oldValue) => {
console.log('newValue: ', newValue)
console.log('oldValue: ', oldValue)
})
</script>
如果想要深度监视对象的内部属性变化,可以在 watch()
第三个参数(选项对象)中设置 deep: true
:
watch(person, (newValue, oldValue) => {
console.log('newValue: ', newValue)
console.log('oldValue: ', oldValue)
},{ deep: true })
监视 ref()
或 reactive()
定义的【对象类型】数据中的某个属性
若该属性不是【对象类型】属性,需要写成函数形式
- 当监视
ref()
或reactive()
定义的对象中的某个非对象类型属性时,需要写成函数形式。 - 因为直接监视一个非对象类型的属性时,
watch()
无法准确追踪其变化。写成函数形式可以确保正确地获取属性值并进行监视。
- 当监视
若该属性是【对象类型】属性
- 可以直接编写属性名进行监视
- 可以写成函数形式
- 如果要深度监视对象类型的属性,必须在
watch()
的选项中设置deep: true
。
<template>
<div>
<p>姓名: {{ person.name }}</p>
<p>年龄: {{ person.age }}</p>
<p>职业:{{ person.details.job }}</p>
<p>年级:{{ person.details.grade }}</p>
<button @click="changeName">修改名字</button>
<button @click="changeJob">修改职业</button>
<button @click="changeGrade">修改年级</button>
<button @click="changeDetails">修改详细信息</button>
</div>
</template>
<script setup lang="ts">
import { reactive, watch } from 'vue';
const person = reactive({
name: '张三',
age: 18,
details: {
job: 'senior high school student',
grade: '高三'
}
});
const changeName = () => {
person.name += '哈'
}
const changeJob = () => {
person.details.job = 'undergraduate'
}
const changeGrade = () => {
person.details.grade = '大一'
}
const changeDetails = () => {
person.details = {
job: 'fresh graduate',
grade: '毕业啦!'
}
}
// 监视person对象的name属性(基本类型),watch()的第一个参数写成函数式(getter函数)
// () => person.name是一个 getter 函数,它返回person对象的name属性值。
watch(() => person.name, (newName, oldName) => {
console.log('newName:', newName, 'oldName:', oldName)
});
// 监视details属性中的job属性,watch()的第一个参数写成函数式(getter函数)
// () => person.details.job是一个 getter 函数,它返回person.details.job的值。
watch(() => person.details.job, (newJob, oldJob) => {
console.log('newJob:', newJob, 'oldJob:', oldJob)
});
// 监视person对象的details属性(对象类型),watch()的第一个参数写成函数式(getter函数)
// () => person.details, 它返回person对象的details属性值。
watch(() => person.details, (newDetails, oldDetails) => {
console.log('newDetails:', newDetails, 'oldDetails:', oldDetails)
});
// 监视person对象的details属性(对象类型),watch()的第一个参数可以直接写变量
watch(person.details, (newDetails, oldDetails) => {
console.log('newDetails:', newDetails, 'oldDetails:', oldDetails)
});
// 监视person对象的details属性(对象类型),watch()的第一个参数可以直接写变量
watch(person.details, (newDetails, oldDetails) => {
console.log('newDetails:', newDetails, 'oldDetails:', oldDetails)
});
</script>
在这个例子中:
watch()
的第一个参数写成函数式(getter
函数)。例如:() => person.name
是一个getter
函数,它返回person
对象的name
属性值。changeJob
、changeGrade
不会触发watch(() => person.details, (newDetails, oldDetails) => {})
,因为没有开启深度监视。changeDetails
会触发两个监视:watch(() => person.details.job, (newJob, oldJob) => {})
watch(() => person.details, (newDetails, oldDetails) => {})
监视多个响应式数据
<template>
<div>
<p>姓名: {{ name }}</p>
<p>年龄: {{ age }}</p>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
let name = ref('张三')
let age = ref(18)
const changeName = () => {
name.value = `${name.value}哈`
}
const changeAge = () => {
age.value++
}
watch([name, age], (newValue, oldValue) => {
console.log('newValue', newValue) // newValue是一个数组:[name, age]
console.log('oldValue', oldValue) // oldValue是一个数组:[name, age]
})
// 当监视多个响应式数据时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:
watch([name, age], ([newName, newAge], [oldName, oldAge]) => {
console.log(`Name changed from ${oldName} to ${newName}`)
console.log(`Age changed from ${oldAge} to ${newAge}`)
})
</script>
监在这个例子中,监视了 name 和 age 两个响应式数据的变化。当其中任何一个数据发生变化时,回调函数会被执行,打印出两个数据的旧值和新值
watchEffect()
基本概念
watchEffect()
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。watchEffect()
自动追踪函数中使用的响应式数据,并在这些数据发生变化时重新执行函数。- 与
watch()
不同,watchEffect()
不需要明确指定要监视的数据源,它会自动分析函数内部的依赖关系(函数中用到哪些属性,那就监视哪些属性)。
返回值
- 返回一个函数,调用这个函数可以停止副作用的执行。
watch()
与 watchEffect()
的区别
数据源
watch()
需要明确指定要监视的数据源,可以是一个响应式数据、一个返回响应式数据的函数或者一个包含多个响应式数据的数组。watchEffect()
不需要明确指定数据源,它会自动追踪函数内部使用的响应式数据。
回调函数参数
watch()
的回调函数接收两个参数:新值和旧值。如果监视多个数据源,新值和旧值分别是一个包含新数据源值的数组和一个包含旧数据源值的数组。watchEffect()
的回调函数不接收新值和旧值参数,它只接收一个用于停止副作用的清理函数作为可选参数。
是否立即执行
watch()
在创建时不会立即执行回调函数,除非设置immediate: true
。watchEffect()
在创建时会立即执行传入的函数。