Vue 3中的watchEffect
是一个非常强大的响应式API,它允许开发者在响应式数据发生变化时自动运行一些副作用(side effects)。watchEffect
与Vue中的另一个APIwatch
相似,但有一些关键区别和特点。下面我会详细解释watchEffect
的工作原理、使用方法以及与watch
的比较。
工作原理
当你使用watchEffect
时,你提供一个函数,Vue会立即执行这个函数,并跟踪函数中使用的任何响应式状态(reactive state)。当这些状态中的任何一个发生变化时,Vue将再次执行这个函数。
基本用法
在Vue组件中使用watchEffect
通常如下所示:
import { watchEffect } from 'vue';
export default {
setup() {
// ...定义响应式状态
watchEffect(() => {
// ...你的副作用逻辑
});
// ...
}
}
详细信息
第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
默认情况下,侦听器将在组件渲染之前执行。设置 flush: 'post'
将会使侦听器延迟到组件渲染之后再执行。详见回调的触发时机。在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置 flush: 'sync'
来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。
-
返回值是一个用来停止该副作用的函数。
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(id.value)
// `cancel` 会在 `id` 更改时调用
// 以便取消之前
// 未完成的请求
onCleanup(cancel)
data.value = await response
})
停止侦听器:
const stop = watchEffect(() => {})
// 当不再需要此侦听器时:
stop()
选项
watchEffect(() => {}, {
flush: 'post',
onTrack(e) {
// 当监听的值被追踪为依赖时触发,有几个值就会执行几次
debugger
},
onTrigger(e) {
// 当监听的值被更改时触发,执行顺序:onTrigger>onInvalidate>函数
debugger
}
})
特点
-
自动侦测依赖:与
watch
不同,watchEffect
不需要明确指定侦听的源。Vue会自动侦测函数内部使用的响应式引用。 -
立即执行:
watchEffect
的回调函数会在声明时立即执行一次,这有助于初始化副作用。 -
清理效果:
watchEffect
接受一个回调函数,该回调函数可以返回一个清理(cleanup)函数,用于在副作用重新运行前清除或重置状态。
使用场景
- 自动更新DOM:当响应式状态改变时自动更新DOM。
- 与外部库集成:例如,当Vue状态改变时自动更新图表或其他UI元素。
- 资源清理:如取消API请求或清理定时器。
与watch
的比较
- 自动侦测:
watchEffect
自动侦测依赖,而watch
需要指定特定的侦听源。 - 执行时机:
watchEffect
在声明时立即执行一次,而watch
默认不执行,除非设置了immediate
选项。 - 适用场景:
watchEffect
更适合于依赖多个源的复杂逻辑,而watch
更适用于侦听特定的数据源并根据变化作出响应。
示例
1、让 watch 和 watchEffect 监听 reactive 定义的值:
watch:
const state = reactive({ count: 0, attr: { name: "" } });
watch(state, (post, pre) => {
console.log(post);
console.log(pre);
console.log("watch 执行了");
});
const clickEvent = () => {
state.count++;
};
当触发 clickEvent 事件改变 state.count 的值时,我们可以从控制台中看到如下结果,说明 watch 响应了 state.count 的变化,但是在初始的时候并没有执行。
watchEffect:
const state = reactive({ count: 0, attr: { name: "" } });
watchEffect(() => {
console.log("watchEffect 执行了");
console.log(state);
});
const clickEvent = () => {
state.count++;
};
点击多次按钮触发 clickEvent 事件,控制台结果如下,说明 watchEffect 在组件第一次执行的时候执行了回调,而在之后并不再响应 state.count 的变化。
说明 watch 可以监听 reactive 定义的值,而 watchEffect 不能。
2、让 watch 和 watchEffect 监听 ref 定义的值。
const count = ref(0);
watch(count, (post, pre) => {
console.log("watch 执行了");
console.log(post);
console.log(pre);
});
const clickEvent = () => {
count.value++;
};
初始化未执行,点击执行。
watchEffect:
const count = ref(0);
watchEffect(() => {
console.log("watchEffect 执行了");
console.log(count);
});
const clickEvent = () => {
count.value++;
};
结果同上,说明 watch 可以响应 ref 定义的值,而 watchEffect 则不能。
3、让 watch 和 watchEffect 响应单一值的变化:
const state = reactive({ count: 0 });
watch(state.count, (post, pre) => {
console.log("watch 执行了");
console.log(post);
console.log(pre);
});
const clickEvent = () => {
state.count++;
};
结果显示无论怎么触发 clickEvent 事件,watch中的回调函数都不会触发,控制台不会打印任何内容。
watchEffect:
const state = reactive({ count: 0 });
watchEffect(() => {
console.log("watchEffect 执行了");
console.log(state.count);
});
const clickEvent = () => {
state.count++;
};
说明 watchEffect 能响应单一的值,而 watch 不能,若要让 watch 响应 count 的变化,需要给第一个参数传入 getter 函数,如下:
const state = reactive({ count: 0 });
watch(
() => state.count,
(post, pre) => {
console.log("watch 执行了");
console.log(post);
console.log(pre);
}
);
const clickEvent = () => {
state.count++;
};
如果 getter 函数 返回的是 state 引用值,而在改变 state.count 的时候并不会修改 state 的引用值,因此不会响应 state.count 的变化,如果要响应,需要传入第三个参数配置 {deep:true},同时代码中的 post 和 pre 的值是一样的,如下:
const state = reactive({ count: 0 });
//不会响应变化
watch(
() => state,
(post, pre) => {
console.log("watch 执行了");
console.log(post);
console.log(pre);
}
);
const clickEvent = () => {
state.count++;
};
const state = reactive({ count: 0 });
//加上了 {deep:true} 可以响应变化
watch(
() => state,
(post, pre) => {
console.log("watch 执行了");
console.log(post);
console.log(pre);
},
{deep:true}
);
const clickEvent = () => {
state.count++;
};
若返回的是引用值,而又需要比较变化前后不同的值,则需要传入 getter 函数返回该对象的深拷贝后的值,如下例子,返回一个数组:
const state = reactive({ count: 0 });
const numbers = reactive([0, 1, 2, 3]);
watch(
() => [...numbers],
(post, pre) => {
console.log("watch 执行了");
console.log(post);
console.log(pre);
}
);
const clickEvent = () => {
numbers.push(1);
};
以上就是vue3中的watch和watchEffect怎么用的详细内容。