1.watch和watchEffect
这两都是直接调用doWatch方法,唯一区别就是第二个参数不同。
export function watchEffect(
effect: WatchEffect,
options?: WatchOptionsBase
): WatchStopHandle {
return doWatch(effect, null, options)
}
export function watch<T = any>(
source: WatchSource<T> | WatchSource<T>[],
cb: WatchCallback<T>,
options?: WatchOptions
): WatchStopHandle {
// 省略参数校验
return doWatch(source, cb, options)
}
2.doWatch
function doWatch(
source: WatchSource | WatchSource[] | WatchEffect,
cb: WatchCallback | null,
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {
// 当前组件实例
const instance = currentInstance
// 将不同类型的监视对象封装成函数,并在函数内部触发相关get,类似计算属性
let getter: () => any
if (isArray(source)) {
getter = () =>
source.map(s => {
if (isRef(s)) {
return s.value
} else if (isReactive(s)) {
// 触发proxy所有不同层级的get
return traverse(s)
} else if (isFunction(s)) {
// 直接执行,但会捕获错误
return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
} else {
__DEV__ && warnInvalidSource(s)
}
})
} else if (isRef(source)) {
getter = () => source.value
} else if (isReactive(source)) {
// 强制深层监听
getter = () => source
deep = true
} else if (isFunction(source)) {
if (cb) {
getter = () =>
callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
} else {
getter = () => {
if (instance && instance.isUnmounted) {
return
}
// 在每次触发getter时,调用onInvalidate传入的函数
if (cleanup) {
cleanup()
}
return callWithErrorHandling(
source,
instance,
ErrorCodes.WATCH_CALLBACK,
[onInvalidate]
)
}
}
} else {
getter = NOOP
__DEV__ && warnInvalidSource(source)
}
if (cb && deep) {
// 如果是watch且是deep则递归触发get
const baseGetter = getter
getter = () => traverse(baseGetter())
}
let cleanup: () => void
const onInvalidate: InvalidateCbRegistrator = (fn: () => void) => {
// 1、副作用即将重新执行时
// 2、侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)
cleanup = runner.options.onStop = () => {
callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
}
}
// 如果是ssr且在node则不做监听,只执行一遍
if (__NODE_JS__ && isInSSRComponentSetup) {
if (!cb) {
getter()
} else if (immediate) {
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
getter(),
undefined,
onInvalidate
])
}
return NOOP
}
let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE
const applyCb = cb
? () => {
if (instance && instance.isUnmounted) {
return
}
const newValue = runner()
if (deep || hasChanged(newValue, oldValue)) {
if (cleanup) {
cleanup()
}
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
newValue,
// 第一次触发传undefined做旧值
oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
onInvalidate
])
oldValue = newValue
}
}
: void 0
let scheduler: (job: () => any) => void
if (flush === 'sync') {
// 会在内部响应式更新后直接触发
// const invoke = (fn: Function) => fn()
scheduler = invoke
} else if (flush === 'pre') {
// 组件更新前执行
scheduler = job => {
if (!instance || instance.isMounted) {
// 异步队列
queueJob(job)
} else {
// pre的时候,第一次调用要在mounted前,所以直接同步调用
job()
}
}
} else {
// 组件更新后运行
scheduler = job => queuePostRenderEffect(job, instance && instance.suspense)
}
// 生成一个effect用于依赖收集和注册
const runner = effect(getter, {
lazy: true,
computed: true,
onTrack,
onTrigger,
// 如果是watch则运行applyCb,否则运行runner
scheduler: applyCb ? () => scheduler(applyCb) : scheduler
})
// 将副作用添加到当前实例的副作用列表中
recordInstanceBoundEffect(runner)
if (applyCb) {
if (immediate) {
applyCb()
} else {
oldValue = runner()
}
} else {
runner()
}
// 返回一个函数用于停用该副作用
return () => {
stop(runner)
if (instance) {
remove(instance.effects!, runner)
}
}
}
2、总结
watch和watchEffect的区别
- watchEffect和computed比较像,都是通过执行副作用函数获取要监听的数据源,而watch是通过第一参数获取要监听的数据源
- watch的参数有3个(数据源,副作用函数,配置),watchEffect只有两个(副作用函数,配置)
- watch的副作用函数接收的参数比watchEffect多两个(新旧值)
- deep和immediate只对watch有用
watch和watchEffect的相同点
- watch 与 watchEffect共享停止侦听,清除副作用 (相应地 onInvalidate 会作为回调的第三个参数传入)、副作用刷新时机和侦听器调试行为。
- 都能通过返回的停止函数停止监听
- 都有onInvalidate传参
- 相同的副作用刷新时机
- 都有flush(刷新时机)、onTrack(被依赖收集时)、onTrigger(被依赖触发时)