Vue3中watch无法监听的解决办法

 更新时间:2023年05月08日 10:46:19   作者:广宝哥  
本文主要介绍了Vue3中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无法监听内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 简单了解前端渐进式框架VUE

    简单了解前端渐进式框架VUE

    这篇文章主要介绍了前端渐进式框架VUE的相关资料,文中讲解的非常细致,帮助大家开始学习VUE,感兴趣的朋友可以了解下
    2020-07-07
  • 解决在vue的mounted中获取对象为null问题

    解决在vue的mounted中获取对象为null问题

    这篇文章主要介绍了解决在vue的mounted中获取对象为null问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • vue实现登录页背景粒子特效

    vue实现登录页背景粒子特效

    这篇文章主要为大家详细介绍了vue实现登录页背景粒子特效,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Vue中实现滚动加载与无限滚动

    Vue中实现滚动加载与无限滚动

    本文主要介绍了Vue中实现滚动加载与无限滚动,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • 浅析Vue 生命周期

    浅析Vue 生命周期

    这篇文章主要介绍了Vue 生命周期的过程,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-06-06
  • vue项目使用luckyexcel预览excel表格功能(心路历程)

    vue项目使用luckyexcel预览excel表格功能(心路历程)

    这篇文章主要介绍了vue项目使用luckyexcel预览excel表格,我总共尝试了2种方法预览excel,均可实现,还发现一种方法可以实现,需要后端配合,叫做KKfileview,本文给大家介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • keep-Alive搭配vue-router实现缓存页面效果的示例代码

    keep-Alive搭配vue-router实现缓存页面效果的示例代码

    这篇文章主要介绍了keep-Alive搭配vue-router实现缓存页面效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • 基于vite2+Vue3编写一个在线帮助文档工具

    基于vite2+Vue3编写一个在线帮助文档工具

    提起帮助文档,想必大家都会想到 VuePress等。但是VuePress是“静态网站生成器”,需要我们自行编写文档,然后交给VuePress变成网站。因此,本文将用vite2+Vue3编写一个在线帮助文档工具,需要的可以参考一下
    2022-03-03
  • vue2 mint-ui loadmore实现下拉刷新,上拉更多功能

    vue2 mint-ui loadmore实现下拉刷新,上拉更多功能

    这篇文章主要介绍了vue2 mint-ui loadmore实现下拉刷新,上拉更多功能,需要的朋友可以参考下
    2018-03-03
  • vue实现公共方法抽离

    vue实现公共方法抽离

    这篇文章主要介绍了vue实现公共方法抽离,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07

最新评论