监听器
Vue3中watch是一个钩子函数,并且新增了一个WatchEffect
1.watch()
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
1.1 语法
// 侦听单个来源
function watch<T>(
source: WatchSource<T>,
callback: WatchCallback<T>,
options?: WatchOptions
): WatchHandle
// 侦听多个来源
function watch<T>(
sources: WatchSource<T>[],
callback: WatchCallback<T[]>,
options?: WatchOptions
): WatchHandle
type WatchCallback<T> = (
newValue: T,
oldValue: T,
onCleanup: (cleanupFn: () => void) => void
) => void
type WatchSource<T> =
| Ref<T> // ref
| (() => T) // getter
| T extends object
? T
: never // 响应式对象
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 默认:false
deep?: boolean | number // 默认:false
flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
once?: boolean // 默认:false (3.4+)
}
interface WatchHandle {
(): void // 可调用,与 `stop` 相同
pause: () => void
resume: () => void
stop: () => void
}
type DebuggerEvent = {
effect: ReactiveEffect
target: object
type:
| TrackOpTypes /* 'get' | 'has' | 'iterate' */
| TriggerOpTypes /* 'set' | 'add' | 'delete' | 'clear' */
key: any
newValue?: any
oldValue?: any
oldTarget?: Map<any, any> | Set<any>
}watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。
参数:
source:侦听器的源。这个来源可以是以下几种:
一个函数,返回一个值
一个 ref
一个响应式对象
或是由以上类型的值组成的数组(监听多个数据)
callback:
- 在发生变化时要调用的回调函数。当监听单个数据源时,这个回调函数接受三个参数:
- newValue:监听的数据改变后的新值
- oldValue:监听的数据改变前的旧值
- onCleanup:一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。
- 当监听多个数据源时,这个回调接受两个数组
- 第一个数组:依次为多个数据源改变后的新值
- 第二个数组:依次为多个数据源改变前的旧值
- 在发生变化时要调用的回调函数。当监听单个数据源时,这个回调函数接受三个参数:
options:一个可选的对象,可包含以下参数
- immediate:在侦听器创建时立即触发回调。第一次调用时旧值是
undefined。 - deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。在 3.5+ 中,此参数还可以是指示最大遍历深度的数字。
- flush:默认为pre,侦听器将在组件渲染之前执行
- post:将会使侦听器延迟到组件渲染之后再执行
- sync:在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器
- onTrack:将在响应属性或引用作为依赖项被跟踪时被调用
- onTrigger:将在侦听器回调被依赖项的变更触发时被调用。
- once:(3.4+) 回调函数只会运行一次。侦听器将在回调函数首次运行后自动停止。
- immediate:在侦听器创建时立即触发回调。第一次调用时旧值是
返回值:返回一个函数,用于停止对数据源的监听
1.2 具体使用
监听一个ref
const num = ref(123)
watch(num, (newVal,oldVal) => {
// ...
})监听一个有返回值的函数,当使用ref或reactive定义对象时使用如下用法,回调只在此函数的返回值变化时才会触发
const state = reactive({ num:123 })
watch(
()=>state.num,
(newVal,oldVal) => {
// ...
}
)监听多个来源
const num = ref(123)
const num2 = ref(456)
const num3 = reactive({ num: 789 })
watch(
[num, num2, () => num3.num],
(
[newNumValue, newNum2Value, newNum3Value],
[oldNumValue, oldNum2Value, oldNum3Value]) => {
console.log(newNumValue, newNum2Value, newNum3Value)
console.log(oldNumValue, oldNum2Value, oldNum3Value)
}
)
num.value = 1
num2.value = 1
num3.num = 1当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层级变更时也能触发,你需要使用 { deep: true } 强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。
const state = reactive({
count: 0,
cate:{
num1:11,
num2:22
}
})
watch(
() => state,
(newValue, oldValue) => {
// newValue === oldValue
},
{ deep: true }
)当直接侦听一个响应式对象时,侦听器会自动启用深层模式:
const state = reactive({ count: 0 })
watch(state, () => {
/* 深层级变更状态所触发的回调 */
})watch() 和 watchEffect()享有相同的刷新时机和调试选项:
watch(source, callback, {
flush: 'post',
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})停止侦听器:
const stop = watch(source, callback)
// 当已不再需要该侦听器时:
stop()副作用清理:
watch(id, async (newId, oldId, onCleanup) => {
const { response, cancel } = doAsyncWork(newId)
// 当 `id` 变化时,`cancel` 将被调用,
// 取消之前的未完成的请求
onCleanup(cancel)
data.value = await response
})3.5+ 中的副作用清理:
import { onWatcherCleanup } from 'vue'
watch(id, async (newId) => {
const { response, cancel } = doAsyncWork(newId)
onWatcherCleanup(cancel)
data.value = await response
})2.watchEffect()
watchEffect不需要指定的数据源,会自动检测副作用函数中用了哪些数据,当这些数据变化时,副作用函数(也就是第一个参数)会自动触发
2.1 语法
function watchEffect(
effect: (onCleanup: OnCleanup) => void,
options?: WatchEffectOptions
): WatchHandle
type OnCleanup = (cleanupFn: () => void) => void
interface WatchEffectOptions {
flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
interface WatchHandle {
(): void // 可调用,与 `stop` 相同
pause: () => void
resume: () => void
stop: () => void
}
type DebuggerEvent = {
effect: ReactiveEffect
target: object
type:
| TrackOpTypes /* 'get' | 'has' | 'iterate' */
| TriggerOpTypes /* 'set' | 'add' | 'delete' | 'clear' */
key: any
newValue?: any
oldValue?: any
oldTarget?: Map<any, any> | Set<any>
}参数:
- effect:要运行的副作用函数。
- onCleanup:用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
- options:一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
- flush:默认为pre,侦听器将在组件渲染之前执行
- post:将会使侦听器延迟到组件渲染之后再执行
- sync:在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器
- onTrack:将在响应属性或引用作为依赖项被跟踪时被调用
- onTrigger:将在侦听器回调被依赖项的变更触发时被调用。
- flush:默认为pre,侦听器将在组件渲染之前执行
返回值:返回值是一个用来停止该副作用的函数。
2.2 具体使用
基本使用
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1清除副作用
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(id.value)
// `cancel` 会在 `id` 更改时调用
// 以便取消之前
// 未完成的请求
onCleanup(cancel)
data.value = await response
})3.5+ 中的副作用清理:
import { onWatcherCleanup } from 'vue'
watch(id, async (newId) => {
const { response, cancel } = doAsyncWork(newId)
onWatcherCleanup(cancel)
data.value = await response
})停止侦听器
const stop = watchEffect(() => {})
// 当不再需要此侦听器时:
stop()配置选项
watchEffect(() => {}, {
flush: 'post',
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})3.watchPostEffect()
watchEffect()使用 flush: 'post' 选项时的别名。
4.watchSyncEffect()
watchEffect()使用 flush: 'sync' 选项时的别名。
5.onWatcherCleanup()
版本需要在3.5+
注册一个清理函数,在当前侦听器即将重新运行时执行。只能在 watchEffect 作用函数或 watch 回调函数的同步执行期间调用 (即不能在异步函数的 await 语句之后调用)。
类型
function onWatcherCleanup(
cleanupFn: () => void,
failSilently?: boolean
): void示例
import { watch, onWatcherCleanup } from 'vue'
watch(id, (newId) => {
const { response, cancel } = doAsyncWork(newId)
// 如果 `id` 变化,则调用 `cancel`,
// 如果之前的请求未完成,则取消该请求
onWatcherCleanup(cancel)
})