<script lang="ts" setup> // 接受父组件传递的数据 const props = defineProps({ test: { type: String, default: '' } }) // 使用 watch 侦听 props 中的 test 属性 watch( // 这种写法不会侦听到 props 中 test 的变化 props.test, () => { console.log("侦听成功") } ) watch( // 这种写法会侦听到 props 中 test 的变化 () => props.test, () => { console.log("侦听成功") } ) </script>
Basic usage of watch
watch () The default is lazy listening, that is, the callback function is only executed when the listening source changes
The first parameter: listening source, listening The listening source can be one of the following types
A function that returns a value, a ref, a responsive object (reactive) or an array composed of the above types of valuesThe second parameter: when the listening source changes The callback function that is triggered.
(newValue, oldValue) => { /* code */}
When listening to multiple sources, the callback function accepts two arrays, corresponding to the new values in the source array. and old values
( [ newValue1, newValue2 ] , [ oldValue1 , oldValue2 ]) => {/* code */}
The third parameter: optional object, can be supported These options
immediate: Trigger the callback immediately when the listener is created deep: If the source is an object, depth traversal is forced so that the callback function is triggered when the deep level changes flush: Adjust the refresh timing of the callback function onTrack/onTrigger: Dependencies of the debugging listener
Because the listening source of watch
can only be the 4 situations above
const obj = reactive({ count: 0 }) // 错误,因为 watch() 中的侦听源是一个 number,最终 source 返回的 getter 函数是一个空,所以就得不到侦听的数据 watch(obj.count, (count) => { console.log(`count is: ${count}`) }) // 正确,主要思想是,将侦听源转化为以上4种类型(转化为getter函数是最简单方便的) watch( () => obj.count, (count) => { console.log(`count is: ${count}`) } )
export function watch<T = any, Immediate extends Readonly<boolean> = false>( source: T | WatchSource<T>, cb: any, options?: WatchOptions<Immediate> ): WatchStopHandle { if (__DEV__ && !isFunction(cb)) { warn( `\`watch(fn, options?)\` signature has been moved to a separate API. ` + `Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` + `supports \`watch(source, cb, options?) signature.` ) } return doWatch(source as any, cb, options) }
It can be seen from the source code that watch
receives three parameters: source
listening source, cb
callback function, options
Listening configuration will eventually return a doWatch
function doWatch( source: WatchSource | WatchSource[] | WatchEffect | object, cb: WatchCallback | null, { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ ): WatchStopHandle { // ... // 当前组件实例 const instance = currentInstance // 副作用函数,在初始化effect时使用 let getter: () => any // 强制触发侦听 let forceTrigger = false // 是否为多数据源。 let isMultiSource = false }
doWatch
still accepts three Parameters: source
Listening source, cb
Callback function, options
Listening configuration
Here we focus on analyzing the source code of the listening source (source standardization)
If source
is a ref
type, getter
is a return# The function of ##source.value,
forceTrigger depends on whether
source is shallow responsive.
if (isRef(source)) { getter = () => source.value forceTrigger = isShallow(source) }
source is of
reactive type,
getter is a return
source function and set
deep to
true. When listening directly to a reactive object, the listener automatically enables deep mode
if (isReactive(source)) { getter = () => source deep = true }
<template> <div class="container"> <h3>obj---{{ obj }}</h3> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> </div> </template> <script lang="ts" setup> import { reactive, watch } from "vue"; const obj = reactive({ name: "张三", age: 18, }); const changeName = () => { obj.name += "++"; }; const changeAge = () => { obj.age += 1; }; // obj 中的任一属性变化了,都会被监听到 watch(obj, () => { console.log("变化了"); }); </script>
source is an array, set
isMultiSource to
true,
forceTrigger depends on whether
source has a
reactive type The data in the
getter function will traverse the
source and do different processing for different types of
source.
if (isArray(source)) { isMultiSource = true forceTrigger = source.some(isReactive) getter = () => source.map(s => { if (isRef(s)) { return s.value } else if (isReactive(s)) { return traverse(s) } else if (isFunction(s)) { return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER) } else { __DEV__ && warnInvalidSource(s) } }) }
source is a
function. When
cb exists,
source will be executed in the
getter function, where
source will be executed through the
callWithErrorHandling function , errors that occur during the execution of
source will be handled in
callWithErrorHandling; if
cb does not exist, in
getter, if the component has Has been uninstalled, directly
return, otherwise judge
cleanup (
cleanup is registered through
onCleanup in
watchEffect Cleanup function), if
cleanup exists, execute
cleanup, then execute
source, and return the execution result.
source will be wrapped by
callWithAsyncErrorHandling. This function will handle errors that occur during the execution of
source. The difference from
callWithErrorHandling is that
callWithAsyncErrorHandling will handle asynchronous errors.
if (isFunction(source)) { if (cb) { getter = () => callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER) } else { // watchEffect getter = () => { // 如果组件实例已经卸载,直接return if (instance && instance.isUnmounted) { return } // 如果清理函数,则执行清理函数 if (cleanup) { cleanup() } // 执行source,传入onCleanup,用来注册清理函数 return callWithAsyncErrorHandling( source, instance, ErrorCodes.WATCH_CALLBACK, [onCleanup] ) } } }
getter will be assigned to an empty function
getter = NOOP __DEV__ && warnInvalidSource(source)
The above is the detailed content of How to use watch to monitor the property value of an object in Vue3. For more information, please follow other related articles on the PHP Chinese website!