watch
- 监听响应式数据变化
import { ref, watch } from 'vue'
const data = ref('')
watch(data, (newData, oldData) => {
console.log(newData, oldData);
})
<div>
<input v-model="data" type="text"><br />
{{ data }}
</div>
- 监听对象
写法一: watch
加第三个参数传一个对象{}
,加deep
为true
,表示深度监听
import { ref, watch } from 'vue'
const obj = ref({
name: {
suit: {
nuwa: 7800
}
}
})
watch(obj, (newObj, oldObj) => {
console.log(newObj, oldObj);
}, { // <-------------------------- watch 的第三个参数
deep: true
})
<div>
<input type="text" v-model="obj.name.suit.nuwa">
{{ obj.name.suit.nuwa }}
</div>
写法二: 使用reactive
,reactive
在源码中已设置deep
为true
,无需再手动设置
import { ref, watch, reactive } from 'vue'
const obj = reactive({ // <----------
name: {
suit: {
nuwa: 7800
}
}
})
watch(obj, (newObj, oldObj) => {
console.log(newObj, oldObj);
})
watch
的第三个参数
watch(obj, (newObj, oldObj) => {
console.log(newObj, oldObj);
}, {
deep: true, // 深度监听
immediate: true,// 立即执行一次
/*
post: 组件更新后执行
pre: 组件更新前执行
sync: 同步执行
*/
flush: "pre" // post pre sync
})
源码
位置: \core-main\packages\runtime-core\src\apiWatch.ts
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.`
)
}
// watch(obj,(newObj,oldObj)=>{...},{deep:true,...})
return doWatch(source as any, cb, options)
}
doWatch
函数
function doWatch(
source: WatchSource | WatchSource[] | WatchEffect | object,
cb: WatchCallback | null,
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {
if (__DEV__ && !cb) {
if (immediate !== undefined) {
warn(
`watch() "immediate" option is only respected when using the ` +
`watch(source, callback, options?) signature.`
)
}
if (deep !== undefined) {
warn(
`watch() "deep" option is only respected when using the ` +
`watch(source, callback, options?) signature.`
)
}
}
const warnInvalidSource = (s: unknown) => {
warn(
`Invalid watch source: `,
s,
`A watch source can only be a getter/effect function, a ref, ` +
`a reactive object, or an array of these types.`
)
}
const instance =
getCurrentScope() === currentInstance?.scope ? currentInstance : null
// const instance = currentInstance
let getter: () => any
let forceTrigger = false
let isMultiSource = false
if (isRef(source)) {
// 是 ref 对象, 直接赋值给 getter
getter = () => source.value
forceTrigger = isShallow(source)
} else if (isReactive(source)) {
// 是 reactive 对象, 把这个对象直接赋值给 getter
// 并且设置 deep 为 true
getter = () => source
deep = true
} else if (isArray(source)) {
// 数组的话就进行一个遍历,去操作每一项
isMultiSource = true
forceTrigger = source.some(s => isReactive(s) || isShallow(s))
getter = () =>
source.map(s => {
if (isRef(s)) {
// 是 ref 对象, 还是直接返回 ref 对象的值
return s.value
} else if (isReactive(s)) {
// 是 reactive 对象, 调用 traverse 函数
// traverse 递归函数
return traverse(s)
} else if (isFunction(s)) {
// 是函数,再进行别的处理加工
return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
} else {
__DEV__ && warnInvalidSource(s)
}
})
} else if (isFunction(source)) {
// 第一个参数传的是函数 watch(()=>{},(newVal,oldVal)=>{...},{...})
if (cb) {
// getter with cb
// 函数存在,进行加工封装
getter = () =>
callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
} else {
// no cb -> simple effect
getter = () => {
if (instance && instance.isUnmounted) {
return
}
if (cleanup) {
cleanup()
}
return callWithAsyncErrorHandling(
source,
instance,
ErrorCodes.WATCH_CALLBACK,
[onCleanup]
)
}
}
} else {
getter = NOOP
__DEV__ && warnInvalidSource(source)
}
// 2.x array mutation watch compat
if (__COMPAT__ && cb && !deep) {
const baseGetter = getter
getter = () => {
const val = baseGetter()
if (
isArray(val) &&
checkCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance)
) {
traverse(val)
}
return val
}
}
if (cb && deep) {
// 函数存在且deep属性为true,继续调用 traverse 递归函数
const baseGetter = getter
getter = () => traverse(baseGetter())
}
let cleanup: () => void
let onCleanup: OnCleanup = (fn: () => void) => {
cleanup = effect.onStop = () => {
callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
}
}
// in SSR there is no need to setup an actual effect, and it should be noop
// unless it's eager or sync flush
let ssrCleanup: (() => void)[] | undefined
if (__SSR__ && isInSSRComponentSetup) {
// we will also not call the invalidate callback (+ runner is not set up)
onCleanup = NOOP
if (!cb) {
getter()
} else if (immediate) {
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
getter(),
isMultiSource ? [] : undefined,
onCleanup
])
}
if (flush === 'sync') {
const ctx = useSSRContext() as SSRContext
ssrCleanup = ctx.__watcherHandles || (ctx.__watcherHandles = [])
} else {
return NOOP
}
}
// 初始化旧值
let oldValue: any = isMultiSource
? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
: INITIAL_WATCHER_VALUE
const job: SchedulerJob = () => {
if (!effect.active) {
return
}
if (cb) {
// watch(source, cb)
// 获取新值
const newValue = effect.run()
if (
deep ||
forceTrigger ||
(isMultiSource
? (newValue as any[]).some((v, i) =>
hasChanged(v, (oldValue as any[])[i])
)
: hasChanged(newValue, oldValue)) ||
(__COMPAT__ &&
isArray(newValue) &&
isCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance))
) {
// cleanup before running cb again
if (cleanup) {
cleanup()
}
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
newValue, // <------- 新值
// pass undefined as the old value when it's changed for the first time
/*
第一次执行
oldValue(旧值) 和 INITIAL_WATCHER_VALUE 的指针是一样的, 是 {}
oldValue(旧值) 是 {}, 就会返回 undefined
*/
oldValue === INITIAL_WATCHER_VALUE
? undefined
: isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE
? []
: oldValue,
onCleanup
])
// 直接赋值,引用地址相同,所以在页面中看到的新值与旧值一样
oldValue = newValue
}
} else {
// watchEffect
effect.run()
}
}
// important: mark the job as a watcher callback so that scheduler knows
// it is allowed to self-trigger (#1727)
job.allowRecurse = !!cb
let scheduler: EffectScheduler
if (flush === 'sync') {
// 同步执行
scheduler = job as any // the scheduler function gets called directly
} else if (flush === 'post') {
// 组件更新后执行,把上面的 job 函数 传给了 queuePostRenderEffect 函数
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
} else {
// default: 'pre'
job.pre = true
if (instance) job.id = instance.uid
scheduler = () => queueJob(job)
}
// 收集依赖完毕,此时 job 函数还不会执行
const effect = new ReactiveEffect(getter, scheduler)
if (__DEV__) {
effect.onTrack = onTrack
effect.onTrigger = onTrigger
}
// initial run
if (cb) {
if (immediate) {
// 当 immediate 属性为 true 时,才会执行 job 函数
job()
} else {
// 没有设置 immediate, 那么就给旧值初始化
oldValue = effect.run()
}
} else if (flush === 'post') {
// 组件更新后才会监听
queuePostRenderEffect(
effect.run.bind(effect),
instance && instance.suspense
)
} else {
effect.run()
}
const unwatch = () => {
effect.stop()
if (instance && instance.scope) {
remove(instance.scope.effects!, effect)
}
}
if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
return unwatch
}