Vue3中如何解决watch无法监听的问题?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1181个文字,预计阅读时间需要5分钟。
下面的代码段展示了在给`ref`实例赋值完成后,为何没有执行`watch`中定义的回调函数:
原因在于`watch`函数作为`value`的setter中的回调,它仅在`test.value`被设置新值时被调用。在上述代码中,虽然`test.value`被赋值为一个新对象`{ name: 1 }`,但`watch`函数没有被直接调用,因为没有通过setter的语法调用了setter中的回调。
要确保`watch`被调用,需要在赋值操作中显式调用setter中的回调。例如,可以这样做:
javascript// 赋值操作时,确保通过setter的语法来调用回调test.value={ name: 1 }; // 这行代码本身不会调用watchtest.value=test.value; // 通过这种方式,setter中的回调会被触发
这样,setter中的`watch`回调就会在赋值操作完成后被调用。
下面的代码在对ref实例赋值完之后。既:
test.value = { name: 1 },会发现不执行watch里面的回调函数了,这是为什么呢?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="../../dist/vue.global.js"></script> </head> <body> <div id="app"> </div> <script> var { createApp, ref, watch, onMounted } = Vue; var app = createApp({ setup() { var test = ref({}); onMounted(() => { test.value = { name: 1 } }) setInterval(() => { test.value.name++ }, 3000) watch(test.value, () => { debugger }) return { test, } } }) app.mount('#app') </script> <script> </script> </body> </html>
1:定义ref类型的响应式var test = ref({}), 然后开始执行watch(test.value, cb)函数
2: 这时候触发了test.value,也就是ref中的get value()方法,该方法会执行trackRefValue(this)
export function trackRefValue(ref: RefBase<any>) { if (shouldTrack && activeEffect) { ref = toRaw(ref) if (__DEV__) { trackEffects(ref.dep || (ref.dep = createDep()), { target: ref, type: TrackOpTypes.GET, key: 'value' }) } else { trackEffects(ref.dep || (ref.dep = createDep())) } } }
- 就是为当前ref实例,收集依赖,但是发现
shouldTrack为false,activeEffect为undefined,所以不执行后面的逻辑
3:触发ref的get value() 方法之后,开始执行watch函数了
3-1: test.value是响应式属性,所以isReactive(source)为true, deep为true 如何包裹一层函数
if (cb && deep) { const baseGetter = getter getter = () => traverse(baseGetter()) }
4: 然后实例化构造函数const effect = new ReactiveEffect(getter, scheduler), 执行effect.run()
执行getter = () => traverse(baseGetter()) 为test.value里面的属性搜集ReactiveEffect依赖
5: onMounted之后,直接替换test.value的值,触发了set value()方法
5-1:对新设置的值,重新定义proxy响应式属性toReactive(newVal)
并且触发triggerRefValue(this, newVal), 触发依赖的执行
set value(newVal) { const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal) newVal = useDirectValue ? newVal : toRaw(newVal) if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) triggerRefValue(this, newVal) } }
但是get value()的时候,没有收集到ReactiveEffect,所以执行triggerRefValue(this, newVal) 的时候,没有执行到watch的回调函数
6:后面执行setInterval对test.value进行赋值的时候,也没有更新watch的回调,因为set value()的时候 重新执行了一次toReactive(newVal) 之前收集的依赖已经失效了
7:解决方法
// 方法一 watch(() => test.value, () => { debugger }, { deep: true }) // 方法二 watch(test, () => { debugger }, { deep: true }) // 方法三, 在test.value赋值之后执行watch var { createApp, ref, watch, onMounted } = Vue; var app = createApp({ setup() { var test = ref({}); onMounted(() => { test.value = { name: 1 } watch(test.value, () => { debugger }) }) setInterval(() => { test.value.name++ }, 3000) // watch(test.value, () => { // debugger // }) return { test, } } }) app.mount('#app')
这个写法,不会一开始就触发ref实例的get value()方法, 而是在创建 const effect = new ReactiveEffect(getter, scheduler) ,执行effect.run() 的时候,触发get value()方法,搜集依赖
7-1:当对test.value = {name: 1}赋值的时候,触发set value()方法,就可以触发triggerRefValue(this, newVal) 执行依赖,从而可以再次对新的值,重新搜集依赖
总结
普通的写法进行监听,对ref的值进行赋值,既:test.value = { name: 1 },在get vlaue()的时候,没有收集 到watch的依赖,在触发set value()的时候,就没有再行watch了
而加了函数包裹test.value,在执行effect.run()的时候,才会触发ref的get value(), 从而可以执行trackRefValue(this)收集到依赖
watch(() => test.value, () => { debugger }, { deep: true }) export function trackRefValue(ref: RefBase<any>) { if (shouldTrack && activeEffect) { ref = toRaw(ref) if (__DEV__) { trackEffects(ref.dep || (ref.dep = createDep()), { target: ref, type: TrackOpTypes.GET, key: 'value' }) } else { trackEffects(ref.dep || (ref.dep = createDep())) } } }
再触发set value()的时候,也就可以重新触发effect.run()了
到此这篇关于Vue3中watch无法监听的解决办法的文章就介绍到这了,更多相关Vue3 watch无法监听内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!
本文共计1181个文字,预计阅读时间需要5分钟。
下面的代码段展示了在给`ref`实例赋值完成后,为何没有执行`watch`中定义的回调函数:
原因在于`watch`函数作为`value`的setter中的回调,它仅在`test.value`被设置新值时被调用。在上述代码中,虽然`test.value`被赋值为一个新对象`{ name: 1 }`,但`watch`函数没有被直接调用,因为没有通过setter的语法调用了setter中的回调。
要确保`watch`被调用,需要在赋值操作中显式调用setter中的回调。例如,可以这样做:
javascript// 赋值操作时,确保通过setter的语法来调用回调test.value={ name: 1 }; // 这行代码本身不会调用watchtest.value=test.value; // 通过这种方式,setter中的回调会被触发
这样,setter中的`watch`回调就会在赋值操作完成后被调用。
下面的代码在对ref实例赋值完之后。既:
test.value = { name: 1 },会发现不执行watch里面的回调函数了,这是为什么呢?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="../../dist/vue.global.js"></script> </head> <body> <div id="app"> </div> <script> var { createApp, ref, watch, onMounted } = Vue; var app = createApp({ setup() { var test = ref({}); onMounted(() => { test.value = { name: 1 } }) setInterval(() => { test.value.name++ }, 3000) watch(test.value, () => { debugger }) return { test, } } }) app.mount('#app') </script> <script> </script> </body> </html>
1:定义ref类型的响应式var test = ref({}), 然后开始执行watch(test.value, cb)函数
2: 这时候触发了test.value,也就是ref中的get value()方法,该方法会执行trackRefValue(this)
export function trackRefValue(ref: RefBase<any>) { if (shouldTrack && activeEffect) { ref = toRaw(ref) if (__DEV__) { trackEffects(ref.dep || (ref.dep = createDep()), { target: ref, type: TrackOpTypes.GET, key: 'value' }) } else { trackEffects(ref.dep || (ref.dep = createDep())) } } }
- 就是为当前ref实例,收集依赖,但是发现
shouldTrack为false,activeEffect为undefined,所以不执行后面的逻辑
3:触发ref的get value() 方法之后,开始执行watch函数了
3-1: test.value是响应式属性,所以isReactive(source)为true, deep为true 如何包裹一层函数
if (cb && deep) { const baseGetter = getter getter = () => traverse(baseGetter()) }
4: 然后实例化构造函数const effect = new ReactiveEffect(getter, scheduler), 执行effect.run()
执行getter = () => traverse(baseGetter()) 为test.value里面的属性搜集ReactiveEffect依赖
5: onMounted之后,直接替换test.value的值,触发了set value()方法
5-1:对新设置的值,重新定义proxy响应式属性toReactive(newVal)
并且触发triggerRefValue(this, newVal), 触发依赖的执行
set value(newVal) { const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal) newVal = useDirectValue ? newVal : toRaw(newVal) if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) triggerRefValue(this, newVal) } }
但是get value()的时候,没有收集到ReactiveEffect,所以执行triggerRefValue(this, newVal) 的时候,没有执行到watch的回调函数
6:后面执行setInterval对test.value进行赋值的时候,也没有更新watch的回调,因为set value()的时候 重新执行了一次toReactive(newVal) 之前收集的依赖已经失效了
7:解决方法
// 方法一 watch(() => test.value, () => { debugger }, { deep: true }) // 方法二 watch(test, () => { debugger }, { deep: true }) // 方法三, 在test.value赋值之后执行watch var { createApp, ref, watch, onMounted } = Vue; var app = createApp({ setup() { var test = ref({}); onMounted(() => { test.value = { name: 1 } watch(test.value, () => { debugger }) }) setInterval(() => { test.value.name++ }, 3000) // watch(test.value, () => { // debugger // }) return { test, } } }) app.mount('#app')
这个写法,不会一开始就触发ref实例的get value()方法, 而是在创建 const effect = new ReactiveEffect(getter, scheduler) ,执行effect.run() 的时候,触发get value()方法,搜集依赖
7-1:当对test.value = {name: 1}赋值的时候,触发set value()方法,就可以触发triggerRefValue(this, newVal) 执行依赖,从而可以再次对新的值,重新搜集依赖
总结
普通的写法进行监听,对ref的值进行赋值,既:test.value = { name: 1 },在get vlaue()的时候,没有收集 到watch的依赖,在触发set value()的时候,就没有再行watch了
而加了函数包裹test.value,在执行effect.run()的时候,才会触发ref的get value(), 从而可以执行trackRefValue(this)收集到依赖
watch(() => test.value, () => { debugger }, { deep: true }) export function trackRefValue(ref: RefBase<any>) { if (shouldTrack && activeEffect) { ref = toRaw(ref) if (__DEV__) { trackEffects(ref.dep || (ref.dep = createDep()), { target: ref, type: TrackOpTypes.GET, key: 'value' }) } else { trackEffects(ref.dep || (ref.dep = createDep())) } } }
再触发set value()的时候,也就可以重新触发effect.run()了
到此这篇关于Vue3中watch无法监听的解决办法的文章就介绍到这了,更多相关Vue3 watch无法监听内容请搜索易盾网络以前的文章或继续浏览下面的相关文章希望大家以后多多支持易盾网络!

