和Vue3和解的Day8--侦听器watchEffect在组合式中

137 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情

说点题外话

上一篇介绍了组合式composition API的写法:

  • setup是一个函数,需要将template模板展示的内容return出去
  • 如果是setup语法糖,则直接定义需要展示的变量或者方法即可

组合式定义响应式变量的两种方式:

  • ref(常用):更适合定义String、Number基本数据类型
  • reactive:适合定义Object等引用数据类型

今天这篇介绍侦听器watch在组合式 API和setup语法糖下面的写法

说正文

image.png

一、watchEffect

watchEffect用于自动收集响应式数据的依赖

1.1 watchEffect基本使用

<template>
  <div>
    <h2>{{ counter }}</h2>
    <button @click="addCounter">+1</button>
  </div>
</template>

<script >
import { ref, watchEffect } from 'vue';

export default{
  setup() {
    const counter = ref(100)

    const addCounter = () => counter.value++

    watchEffect(() => {
      console.log("counter:", counter.value);
    })

    return {
      counter,
      addCounter
    }
  }
}

</script>

image.png

从上面的代码和输出结果能看出来,watchEffect会默认执行一次。检测到有使用的变量,就会将变量添加到依赖中,只要有变量发生变化,就会执行侦听

1.2 watchEffect的其他参数

1.2.1 停止侦听

watchEffect会返回一个返回值,该返回值是一个函数,在满足条件的时候触发返回值函数。我们可以利用这个返回值做一些操作

这里我们想要实现计数器增加到15的时候停止侦听

<template>
  <div>
    <h2>{{ counter }}</h2>
    <button @click="addCounter">+1</button>
  </div>
</template>

<script >
import { ref, watchEffect } from 'vue';

export default{
  setup() {
    const counter = ref(10)

    const addCounter = () => {
      counter.value++
      if(counter.value >= 15) stop()
    }

    const stop = watchEffect(() => {
      console.log("counter:", counter.value);
    })

    return {
      counter,
      addCounter
    }
  }
}

</script>

image.png 自行定义了一个stop接收watchEffect的返回值,在手动触发计数器到15之后不再侦听变量的改变。

1.2.2 消除副作用

我们做一个假设,假设我们需要根据counter的值去触发对应的网络请求,但是在请求数据的过程中,我们再次触发方法改变了counter的值,我们该如何做才能让每次请求回来的都是正确的数据呢?

我们也可以这么想,也就是我们只要改变了age的值,就需要清除上一次的请求,重新执行一次请求。

watchEffect中有一个参数可以作为消除副作用,它会返回一个函数

<template>
  <div>
    <h2>{{ counter }}</h2>
    <button @click="addCounter">+1</button>
  </div>
</template>

<script >
import { ref, watchEffect } from 'vue';

export default{
  setup() {
    const counter = ref(10)

    const addCounter = () => {
      counter.value++
    }

    watchEffect( onInvalidate => {
      onInvalidate(() => {
        console.log("onInvalidate");
      })
      console.log("counter:", counter.value);
    })

    return {
      counter,
      addCounter
    }
  }
}

</script>

image.png

这里可以发现onInvalidate这个函数不会默认执行一次,我们可以利用这个函数的特性完成我们上面提过的网络请求案例

<template>
  <div>
    <h2>{{ counter }}</h2>
    <button @click="addCounter">+1</button>
  </div>
</template>

<script >
import { ref, watchEffect } from 'vue';

export default {
  setup() {
    const counter = ref(10)

    const addCounter = () => {
      counter.value++
    }

    watchEffect(onInvalidate => {
      const timer = setTimeout(() => {
        // 定时器模拟网络请求
        console.log("setTimeout");
      }, 2000)

      onInvalidate(() => {
        clearTimeout(timer)
      })

      console.log("counter:", counter.value);
    })

    return {
      counter,
      addCounter
    }
  }
}

</script>

image.png

从状态栏的输出我们也可以发现,当触发了button改变counter的值之后,只要我们再次改变counter的值,定时器模拟的网路请求并不会执行。

二、watch

watch的API完全等同于组件watch选项中的property。watch需要侦听特定的数据源,并在回调中执行副作用;默认情况下它是惰性的,只有当被侦听的源发生变化时才会 执行回调。

watch的三个参数:

  • 第一个参数:侦听的源

    • 一个getter函数,返回一个值
    • 一个ref对象或者一个reactive对象
    • 或者是以上类型的值组成的数组
  • 第二个参数:发生变化的时候要调用的回调函数,该回调函数接收三个值(按照顺序):

    • 新值
    • 旧值
    • 用于注册副作用的回调函数
  • 第三个参数:

    • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined
    • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器
    • flush:调整回调函数的刷新时机。pre | post | sync
    • onTrack / onTrigger:调试侦听器的依赖。

我们一个一个参数的详细介绍一下

2.1侦听一个getter函数

<template>
  <div>
    <h2>{{ counter }}</h2>
    <button @click="addCounter">+1</button>
  </div>
</template>

<script >
import { ref, watch } from 'vue';

export default {
  setup() {
    const counter = ref(10)

    const addCounter = () => {
      counter.value++
    }
    
    watch(() => counter, (newValue, oldValue) => {
      console.log("newValue:", newValue, "oldValue:", oldValue);
    })
   

    return {
      counter,
      addCounter
    }
  }
}

</script>

image.png 使用getter函数直接侦听一个ref对象,无法触发侦听器

<template>
  <div>
    <h2>{{ info.name }}</h2>
    <button @click="changeName">+1</button>
  </div>
</template>

<script >
import { watch,reactive } from 'vue';

export default {
  setup() {

    const info = reactive({
      name: "小白"
    })

    const changeName = () => {
      info.name = "小刚"
    }
    
    watch(() => info.name, (newValue, oldValue) => {
      console.log("newValue:", newValue, "oldValue:", oldValue);
    })
   

    return {
      info,
      changeName
    }
  }
}

</script>

image.png 当getter函数接收的是一个对象的时候,可以触发侦听器

image.png

说再见

关于watch的侦听有很多细节,贪多嚼不烂,还是老规矩放在下一篇在继续说侦听器watch

难忘今宵

十恶不赦”指的是哪“十恶”?

“十恶”是在中国封建社会十类重罪的总称。包含谋反、谋大逆、谋叛、恶逆、不道、大不敬、不孝、不睦、不义、内乱。