watch
的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter
函数、或多个数据源组成的数组。
watch()
侦听一个ref
、计算属性、getter
函数
watch(ref变量, (newValue, oldValue) => {})
。当ref
值发生变化时会触发相应的回调(回调中可进行异步操作)。
<script>
import { ref, watch } from "vue";
const count = ref<number>(0);
const double = computed(() => count.value * 2);
const obj = reactive({
count: 0,
});
// 1. 可以单个侦听
watch(count, async (newVal, oldVal) => {
try {
const res = await fetch('https://yesno.wtf/api')
console.log("res...", res);
} catch (error) {
console.log("error...", error);
}
});
watch(double, async (newVal, oldVal) => {
console.log("double变化了...", newVal, oldVal);
});
watch(() => obj.count, async (newVal, oldVal) => {
console.log("obj.count变化了...", newVal, oldVal);
});
// 2. 可以写在一个watch里,然后通过判断对应下标的新旧值变化分别执行不同操作
watch([count, double, () => obj.count], async (newVal, oldVal) => {
// 判断count变化
if (newVal[0] !== oldVal[0]) {
console.log("count变化了...");
}
// 判断double是否变化
if (newVal[1] !== oldVal[1]) {
console.log("double变化了...");
}
// 判断obj.count变化了
if (newVal[2] !== oldVal[2]) {
console.log("obj.count变化了...", newVal[2], oldVal[2]);
}
});
// 3. 或者写在一个watch里,然后通过解构的方法拿到对应的新旧值
watch(
[count, double, () => obj.count],
([newCount, newDouble, newObjCount], [oldCount, oldDouble, oldObjCount]) => {
console.log(newCount, newDouble, newObjCount);
console.log(oldCount, oldDouble, oldObjCount);
}
);
</script>
侦听一个reactive
1、 默认隐式创建一个深层侦听器
直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:
<script>
import { reactive, watch } from "vue";
const obj = reactive({
count: 10,
someObject: {
count: 20,
},
});
const increceCount = function () {
// obj底下任何key值被修改都会触发watch
obj.someObject.count++;
};
watch(
obj,
(newValue, oldValue) => {
console.log("obj变化了...", newValue, oldValue);
}
);
</script>
一个返回响应式对象的 getter 函数,只有在返回不同的对象时,才会触发回调:
<script>
import { reactive, watch } from "vue";
const obj = reactive({
count: 10,
someObject: {
count: 20,
},
});
const increceCount = function () {
// 1. 只有当obj.someObject被替换时才会触发watch
obj.someObject = { ...obj.someObject };
// 2. 像这种直接修改某个key值的不会触发watch
obj.someObject.count++;
};
watch(
() => obj.someObject,
(newValue, oldValue) => {
console.log("obj变化了...", newValue, oldValue);
}
);
</script>
2、 显性创建一个深层侦听器
传入第三个参数加上 deep
选项,强制转成深层侦听器:
<script>
import { reactive, watch } from "vue";
const obj = reactive({
count: 10,
someObject: {
count: 20,
},
});
const increceCount = function () {
// 直接修改某个key值的会触发watch
obj.someObject.count++;
};
watch(
() => obj.someObject,
(newValue, oldValue) => {
console.log("obj变化了...", newValue, oldValue);
},
{
deep: true,
// immediate 为 true 立即执行,且当 obj.someObject 改变时再次执行
immediate: true,
}
);
</script>
注意:当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。
watchEffect()
回调函数里面执行一些异步操作(副作用逻辑)时,可以省略第一个参数,第一个 await
使用过程及其之前用到的所有响应式变量发生变化都会被触发。
const count = ref<number>(0);
const name = ref<string>("");
const age = ref<number>(10);
const obj = reactive({
count: 10,
someObject: {
count: 20,
},
});
const increceCount = function () {
count.value++;
// 只改变obj深层的变量,await及其之前没有使用到someObject的内容,watchEffect回调不会被触发(浅层监听)
// 包括直接使用obj,obj.someObject,使用obj.someObject.count则可以监听到
obj.someObject.count++;
};
watchEffect(async () => {
// count name obj.count 变化都会触发watchEffect回调
console.log(count.value);
const res = await fetch(
`https://yesno.wtf/api?count=${obj.count}&name=${name.value}`
);
// age变化不会触发回调
age.value++;
console.log("watchEffect res1...", res, age.value);
});
watchPostEffect() 和 watchSyncEffect()
watch
的第三个参数,以及 watchEffect
的第二个参数里面都可以添加相应的配置实现 watchPostEffect()
和 watchSyncEffect()
,也可以说 watchPostEffect()
和 watchSyncEffect()
是 watch
和 watchEffect
添加不同参数之后的简写。
1、 watchPostEffect()
相当于传入 flush: 'post'
,可以在 watch
和 watchEffect
回调中获取到更新之后的 dom
,也就是回调会在 dom
更新之后执行(默认在 dom
更新之前执行,也就是 flush: 'pre'
。参数说明传送门)。
watch(source, callback, {
flush: 'post'
})
watchEffect(callback, {
flush: 'post'
})
2、 watchSyncEffect()
使用 flush: 'sync'
选项时的别名,在响应式依赖发生改变时立即触发侦听器(谨慎使用,如果有多个属性同时更新,将导致一些性能和数据一致性的问题)。
watch(source, callback, {
flush: 'sync'
})
watchEffect(callback, {
flush: 'sync'
})