在Vue3中,watch是一个函数,这个函数接收三个参数,前两个是必须的参数,第三个是一个配置参数。
export function watch(source,callback,options = {} as any) {
return doWatch(source,callback,options)
}
设置doWatch目的是为了与watch相似的API ,watchEffect准备,便后后续代码书写
function doWatch(source,callback,{ deep }) {
let getter;
// 判断传入是否是一个函数
if(isFunction(source)) {
// 判断当前函数返回值是否是简单数据
const result = source()
if(isObject(result)) {
source = result
} else {
getter = source
}
}
//判断当前source是否为对象
if(isObject(source)) {
const reactiveGetter = (source) => traverse(source,deep === false ? 1 : undefined)
getter = () => reactiveGetter(source)
}
// 执行的工具函数
const job = () => {
// 执行逻辑
callback()
}
// 创建依赖对象
const effect = new ReactiveEffect(getter,job)
}
function traverse(source, depth,currentDepth = 0,seen = new Set()) {
// 判断是否为对象
if(!isObject(source)) return source
if(depth) {
if(currentDepth >= depth) {
return source
}
currentDepth++
}
// 判断是否循环引用
if(seen.has(source)) return source
for(let key in source) {
traverse(source[key],depth,currentDepth,seen)
}
seen.add(source)
return source
}
在effect创建之前,我们创建一个oldValue变量用于存放老值
然后我们主动调用一次effect.run()获取到当前数据
在job调用时我们再次手动调用effect.run获取到新值,然后通过callback函数将新值与老值传入
function doWatch(source,callback,{ deep }) {
...
// 保存老值
let oldValue;
// 执行的工具函数
const job = () => {
// 获取新值
const newValue = effect.run()
// 执行逻辑
callback(newValue,oldValue)
oldValue = newValue // 替换老值
}
// 创建依赖对象
const effect = new ReactiveEffect(getter,job)
oldValue = effect.run()
}
watch只能够监听响应式数据,因此我们需要在getter赋值时来判断当前数据是否是响应式,如果不是响应时则不做处理
还有一种可能就是如果传入的第一个参数是一个函数,第二个参数不是函数或者不传,这就会使用watchEffectAPI,因此我们还需要再判断
# apiWatch.ts
function doWatch(source,callback,{ deep }) {
...
// 判断是否是响应式对象
if(isReactive(source)) {
getter = () => reactiveGetter(source)
} else if(isRef(source)) {
getter = () => source.value
}
...
const job = () => {
if(callback) {
// 获取新值
const newValue = effect.run()
// 执行逻辑
callback(newValue,oldValue)
oldValue = newValue // 替换老值
} else {
// watchEffect
}
}
...
// 判断当前是否传入回调函数
if(callback) {
} else {
// watchEffect 处理
}
}
// 判断如果是需要立即执行,则先执行一次job函数,将新旧值传入
if(immediate) {
job()
} else {
oldValue = effect.run()
}
export function watchEffect(getter, options = {}) {
return doWatch(getter,null,options as any)
}
function doWatch(source,callback,{ deep }) {
...
// 创建依赖对象
const effect = new ReactiveEffect(getter,job)
// 判断是否需要立即执行
if(immediate) {
job()
} else {
oldValue = effect.run()
}
// 返回一个stop函数
return () => {
effect.stop()
}
}