主题
侦听器
watch
基本使用
js
import { ref, reactive, watch } from 'vue'
// 单个 ref
const question = ref('')
watch(question, async (newVal, oldVal) => {
// ...
})
// getter 函数
const x = ref(0)
const y = ref(0)
watch(
() => x.value + y.value,
(val) => {
// ...
},
)
// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => {
console.log(`x is ${newX} and y is ${newY}`)
})
// 不能直接侦听响应式对象的属性值,需要用一个返回该属性的 getter 函数
const obj = reactive({ count: 0 })
watch(
() => obj.count,
(count) => {
console.log(`Count is: ${count}`)
},
)深层侦听器
显式地加上 deep 选项,强制转成深层侦听器:
js
watch(
() => state.someObject,
(newVal, oldVal) => {
// 注意:`newVal` 此处和 `oldVal` 是相等的
// *除非* state.someObject 被整个替换了
},
{ deep: true },
)在 Vue 3.5+ 中,deep 选项还可以是一个数字,表示最大遍历深度 —— 即 Vue 应该遍历对象嵌套属性的级数。
即时回调的侦听器
js
watch(
source,
(newVal, oldVal) => {
// 立即执行,且当 `source` 改变时再次执行
},
{ immediate: true },
)watchEffect
js
watchEffect(async () => {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
data.value = await response.json()
})在上述代码中,回调会立即执行,不需要指定 immediate: true。在执行期间,它会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。
清理副作用
js
// 并且必须在 `watchEffect` 效果函数或 `watch` 回调函数的同步执行期间调用:不能在异步函数的 `await` 语句之后调用它。
import { watch, onWatcherCleanup } from 'vue'
watch(id, (newId) => {
const controller = new AbortController()
fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
// 回调逻辑
})
onWatcherCleanup(() => {
// 终止过期请求
controller.abort()
})
})js
// 通过函数参数传递的 onCleanup 与侦听器实例相绑定,因此不受 onWatcherCleanup 的同步限制。
watch(id, (newId, oldId, onCleanup) => {
onCleanup(() => {
// 清理逻辑
})
})
watchEffect((onCleanup) => {
onCleanup(() => {
// 清理逻辑
})
})回调的触发时机
post 侦听器
如果想在侦听器回调中能访问被 Vue 更新之后的所属组件的 DOM:
js
watch(source, callback, {
flush: 'post',
})
watchEffect(callback, {
flush: 'post',
})
watchPostEffect(() => {
/* 在 Vue 更新后执行 */
})async 侦听器
在 Vue 进行任何更新之前触发:
js
watch(source, callback, {
flush: 'sync',
})
watchEffect(callback, {
flush: 'sync',
})
watchSyncEffect(() => {
/* 在响应式数据变化时同步执行 */
})停止侦听器
js
const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()常见问题
watch 和 watchEffect 的区别?
watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
watch只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。
