Vue3 中 watch 与 watchEffect 有什么区别?

学习vue的笔记

1.watch是惰性执行,也就是只有监听的值发生变化才会执行,但watchEffect不同,每次代码加载watchEffect都会执行。

注:(忽略watch第三个参数的配置,如果修改配置项,也可以实现立即执行)。

2.watch需要传递监听的对象,watchEffect不需要。

3.watch只能监听响应式数据:ref定义的属性和reactive定义的对象,如果直接监听reactive定义对象中的属性是不允许的 (会报警告),除非使用函数转换一下,其实就是官网上说的监听一个getter。

4.watchEffect如果监听reactive定义的对象是不起作用的 ,只能监听对象中的属性。

先看一下watchEffect的代码,验证一下

<template>
<div>
  请输入firstName:
  <input type="text" v-model="firstName">
</div>
<div>
  请输入lastName:
  <input type="text" v-model="lastName">
</div>
<div>
  请输入obj.text:
  <input type="text" v-model="obj.text">
</div>
 <div>
 【obj.text】 {
   
   {obj.text}}
 </div>
</template>
 

以下的方法都用三个 标签做返回。

<script>
import {ref, reactive, watch, watchEffect} from 'vue'
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup(props,content){
    let firstName = ref('')
    let lastName = ref('')
    let obj= reactive({
        text:'hello'
      })
    watchEffect(()=>{
      console.log('触发了watchEffect');
      console.log(`组合后的名称为:${firstName.value} ${lastName.value}`)
    })
    return{
      obj,
      firstName,
      lastName
    }
  }
};
</script>

<script>
import {ref, reactive, watch, watchEffect} from 'vue'
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup(props,content){
    let firstName = ref('')
    let lastName = ref('')
    let obj= reactive({
        text:'hello'
      })
    watchEffect(()=>{
      console.log('触发了watchEffect');
      // 这里我们不使用firstName.value/lastName.value ,相当于是监控整个ref,对应第四点上面的结论
      console.log(`组合后的名称为:${firstName} ${lastName}`)
    })
    return{
      obj,
      firstName,
      lastName
    }
  }
};
</script>

<script>
import {ref, reactive, watch, watchEffect} from 'vue'
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup(props,content){
    let firstName = ref('')
    let lastName = ref('')
    let obj= reactive({
        text:'hello'
      })
    watchEffect(()=>{
      console.log('触发了watchEffect');
      console.log(obj);
    })
    return{
      obj,
      firstName,
      lastName
    }
  }
};
</script>

<script>
import {ref, reactive, watch, watchEffect} from 'vue'
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup(props,content){
    let firstName = ref('')
    let lastName = ref('')
    let obj= reactive({
        text:'hello'
      })
    //watchEffect(()=>{
     // console.log('触发了watchEffect');
     // console.log(obj);
   // })
watchEffect(()=>{
      console.log('触发了watchEffect');
      console.log(obj.text);
    })
    return{
      obj,
      firstName,
      lastName
    }
  }
};
</script>

 再看一下watch的代码,验证一下

<template>
<div>
  请输入firstName:
  <input type="text" v-model="firstName">
</div>
<div>
  请输入lastName:
  <input type="text" v-model="lastName">
</div>
<div>
  请输入obj.text:
  <input type="text" v-model="obj.text">
</div>
 <div>
 【obj.text】 {
   
   {obj.text}}
 </div>
</template>

以下方法都在这三个标签中回显 


<script>
import {ref, reactive, watch, watchEffect} from 'vue'
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup(props,content){
    let firstName = ref('')
    let lastName = ref('')
    let obj= reactive({
        text:'hello'
      })
   // watch是惰性执行, 默认初始化之后不会执行,只有值有变化才会触发,可通过配置参数实现默认执行
    watch(obj, (newValue, oldValue) => {
      // 回调函数
      console.log('触发监控更新了new',  newValue);
      console.log('触发监控更新了old',  oldValue);
    })
    return{
      obj,
      firstName,
      lastName
    }
  }
};
</script>

配置immediate参数,立即执行,以及深层次监听

// 配置immediate参数,立即执行,以及深层次监听
 watch(obj, (newValue, oldValue) => {
      // 回调函数
      console.log('触发监控更新了new',  newValue);
      console.log('触发监控更新了old',  oldValue);
    }, {
      immediate: true,
      deep: true
    })

监控整个reactive对象,从上面的图可以看到 deep 实际默认是开启的,就算我们设置为false也还是无效。而且旧值获取不到。

要获取旧值则需要监控对象的属性,也就是监听一个getter,看下图

<script>
import { ref, reactive, watch, watchEffect } from "vue";
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup(props, content) {
    let firstName = ref("");
    let lastName = ref("");
    let obj = reactive({
      text: "hello",
    });

    //错误写法,会报警告,这不是监听对象的一个get

    // watch(
    //   obj.text,
    //   (newValue, oldValue) => {
    //     // 回调函数
    //     console.log("触发监控更新了new", newValue);
    //     console.log("触发监控更新了old", oldValue);
    //   },
    //   {
    //     immediate: true,
    //     deep: false,
    //   }
    // );

    //正确写法

    watch(
      () => obj.text,
      (newValue, oldValue) => {
        // 回调函数
        console.log("触发监控更新了new", newValue);
        console.log("触发监控更新了old", oldValue);
      },
      {
        immediate: true,
        deep: true,
      }
    );

    return {
      obj,
      firstName,
      lastName,
    };
  },
};
</script>

总结:

如果定义了reactive的数据,想去使用watch监听数据改变,则无法正确获取旧值,并且deep属性配置无效,自动强制开启了深层次监听。

如果使用ref初始化一个对象或者数组类型的数据,会被自动转成reactive的实现方式,生成proxy代理对象。也会变得无法正确获取旧值

用任何方式生成的数据,如果接受的变量是一个proxy代理对象,就都会导致watch这个对象时,watch回调里无法正确获取旧值

所以当使用watch监听对象时 ,如果在不需要使用旧值的情况下,可以正常监听对象。

当监听改变函数里面需要用到的旧值时,只能监听 obj.XXX 的方式才行

不理解的参考更多地址: Vue3 中 watch 与 watchEffect 有什么区别?_普通网友的博客-CSDN博客

猜你喜欢

转载自blog.csdn.net/snow_living/article/details/125599975