下面的代码在对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="https://www.atool.online/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无法监听内容请搜索阿兔在线工具以前的文章或继续浏览下面的相关文章希望大家以后多多支持阿兔在线工具!

点赞(0)

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部