美文网首页
24.compositionApi的watchEffect和re

24.compositionApi的watchEffect和re

作者: 静昕妈妈芦培培 | 来源:发表于2021-08-30 11:42 被阅读0次

侦听数据的变化

在前面的Options API中,我们可以通过watch选项来侦听data或者props的数据变化,当数据变化时执行某一些操作。

在Composition API中,我们可以使用watchEffectwatch来完成响应式数据的侦听
  • watchEffect用于自动收集响应式数据的依赖;
  • watch需要手动指定侦听的数据源;

watchEffect

当侦听到某些响应式数据变化时,我们希望执行某些操作,这个时候可以使用 watchEffect

  • 首先,watchEffect传入的函数在页面初次渲染的时候会被立即执行一次,并且在执行函数的过程中会收集依赖,这些依赖为响应式对象
  • 其次,只要收集的依赖中不管哪个发生变化,watchEffect传入的函数都会再次执行;

watchEffect的基本使用

watchEffect的回调参数会在页面首页渲染的时候执行一次,通过这次执行,收集回调中依赖的响应式变量,然后监听其变化,一但侦听到变量值的改变,就会重新执行其回调函数
例:

<template>
  <div>
    <h1>名字: {{ name }} - 年纪: {{ age }}</h1>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年纪</button>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";
export default {
  setup() {
    const name = ref("why");
    const age = ref(18);

    //watchEffect的回调参数会在页面首页渲染的时候执行一次,通过这次执行,收集回调中依赖的响应式变量,然后监听其变化,
    //一但侦听到变量值的改变,就会重新执行其回调函数
    watchEffect(() => {
      //此函数中依赖的响应式对象为name和age,所以,不管name和age哪个的值发生改变,此回调函数都会重新执行
      console.log("name-", name.value, "age-", age.value);
    });

    const changeName = () => (name.value = "coder");
    const changeAge = () => age.value++;

    return {
      name,
      age,
      changeName,
      changeAge,
    };
  },
};
</script>

<style lang="scss" scoped></style>

watchEffect的停止侦听

watchEffect函数的返回值也是一个函数,调用此函数会停止侦听
如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可。
案例:当age达到20的时候就停止侦听

<template>
  <div>
    <h1>名字: {{ name }} - 年纪: {{ age }}</h1>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年纪</button>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";
export default {
  setup() {
    const name = ref("why");
    const age = ref(18);
    //watchEffect函数会返回一个函数,调用此函数会停止侦听
    const stop = watchEffect(() => {
      console.log("age-", age.value);
    });

    const changeName = () => (name.value = "coder");
    const changeAge = () => {
       if(age.value > 20) {
          stop() //当age大于20时,停止侦听
       }
       age.value++
    };

    return {
      name,
      age,
      changeName,
      changeAge,
    };
  },
};
</script>

<style lang="scss" scoped></style>

watchEffect清除副作用

什么是清除副作用呢?
  • 比如在开发中我们需要在侦听函数中执行网络请求,但是在网络请求还没有达到的时候,我们停止了侦听器,或者侦听器侦听函数被再次执行了。
  • 那么上一次的网络请求应该被取消掉,这个时候我们就可以清除上一次的副作用;
在我们给watchEffect传入的函数被回调时,其实可以获取到一个参数:onInvalidate
onInvalidate也是一个函数,接收一个回调函数作为参数
  • 当副作用即将重新执行 或者 侦听器被停止 时会执行onInvalidate函数传入的回调函数
  • 我们可以在传入的回调函数中,执行一些清除工作;
  • 当watchEffect依赖的响应式对象的值改变时,执行其回调函数前,会先执行回调接受的onInvadiate函数
<template>
  <div>
    <h1>名字: {{ name }} - 年纪: {{ age }}</h1>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年纪</button>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";
export default {
  setup() {
    const name = ref("why");
    const age = ref(18);
    let timer = null;

    //watchEffect的回调函数参数在执行的时候也接收一个onInvalidate函数,
    //onInvalidate函数也接收一个回调函数作为参数
    //可以在onInvadiate函数的回调函数中做一些取消的处理,比如取消请求
    //当watchEffect侦听的变量值改变时,执行其回调函数前,会先执行回调接受的onInvadiate函数
    watchEffect((onInvadiate) => {
      timer = setTimeout(() => {
        console.log("模拟发送请求,携参:name,age");
      }, 3000);
      console.log("name-", name.value, "age-", age.value);
      onInvadiate(() => {
        //不管onInvadiate在watchEffect回到函数中的位置,只要回调函数执行,都优先执行onInvadiate
        //取消上次发送的请求
        clearTimeout(timer);
        console.log("onInvalidate");
      });
    });

    const changeName = () => (name.value = "coder");
    const changeAge = () => age.value++;

    return {
      name,
      age,
      changeName,
      changeAge,
    };
  },
};
</script>

<style lang="scss" scoped></style>

image.png

setup中使用ref

在讲解 watchEffect执行时机之前,我们先补充一个知识:在setup中如何使用ref或者元素或者组件?

  • 其实非常简单,我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可;
<template>
  <div>
    <h1 ref="h1">hello world</h1>
  </div>
</template>

<script>
  import {ref} from 'vue'
  export default {
    setup() {
      const h1 = ref(null)
      return {
        h1
      }
    }
  }
</script>

watchEffect的执行时机

默认情况下,组件的更新会在副作用函数执行之后:
例:

默认情况下watchEffect会在组件挂载之前执行,所以watchEffect的回调会执行两次:

  • 第一次执行时,组件还没挂载,h1.value为null
  • 组加挂载成功后,h1.value的值变为h1Dom元素,watchEffect会再次执行,因为watchEffect依赖h1这个响应式对象
<template>
  <div>
    <h1 ref="h1">hello world</h1>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";
export default {
  setup() {
    let h1 = ref(null);
    //默认情况下watchEffect会在组件挂载之前执行,
    //所以watchEffect的回调会执行两次:
    //第一次执行时,组件还没挂载,h1.value为null
    //组加挂载成功后,h1.value的值变为h1Dom元素,watchEffect会再次执行,
    //因为watchEffect依赖h1这个响应式对象
    watchEffect(() => {
      //h1为h1DOM元素
      console.log("h1", h1.value);
    });

    return {
      h1,
    };
  },
};
</script>

<style lang="scss" scoped></style>

我们会发现打印结果打印了两次:
  • 这是因为setup函数在执行时就会立即执行传入的副作用函数,这个时候DOM并没有挂载,所以打印为null;
  • 而当DOM挂载时,会给title的h1对象赋值新的值,副作用函数会再次执行,打印出来对应的元素;


    image.png

调整watchEffect的执行时机

如果我们希望在第一次的时候就打印出来对应的元素呢?
  • 这个时候我们需要改变副作用函数的执行时机;

watchEffect函数接收第二个参数为一个对象,此对象中可以做一些配置

  • flush选项的默认值是pre,它决定watchEffect的回调函数会在元素挂载 或者 更新 之前执行;
    • 所以我们会先打印出来一个空的,当依赖的title发生改变时,就会再次执行一次,打印出元素;
  • flush: "post"调整watchEffect的执行时机为元素挂载/更新之后执行
  • flush 选项还接受 sync,这将强制效果始终同步触发。然而,这是低效的,应该很少需要。
例:
<template>
  <div>
    <h1 ref="h1">hello world</h1>
  </div>
</template>

<script>
  import {ref, watchEffect} from 'vue'
  export default {
    setup() {
      let h1 = ref(null)
      watchEffect(() => {
        //h1为h1DOM元素
        console.log('h1', h1.value)
      }, {
        flush: 'post'//调整watchEffect的执行时机为元素挂载/更新之后执行
        //默认为'pre'  元素挂载/更新之前执行
      })

      return {
        h1
      }
    }
  }
</script>

<style lang="scss" scoped>

</style>
我们会发现第一次就获取到了元素

因为watchEffect的回调函数是在元素挂载之后才执行的,此时h1.value已经是h1dom元素了


image.png

此文档主要内容来源于王红元老师的vue3+ts视频教程

相关文章

网友评论

      本文标题:24.compositionApi的watchEffect和re

      本文链接:https://www.haomeiwen.com/subject/phvfiltx.html