Vue3 watch 的六大特点

199 阅读4分钟

首先回顾下watch 的作用, 监听数据的变化, 然后可以做一些操作,用官方的话来说就是执行一些副作用。 来看一个简单的案例

<script setup>
import { ref, watch } from 'vue'
const number = ref(0)
const showHello = ref(false)
watch(number, (newVal, oldVal) => {
  showHello.value = newVal % 2 === 0
})

setInterval(() => {
  number.value++
}, 2000)
</script>

<template>
  <p>{{ number }}</p>
  <p v-if="showHello">hello</p>
</template>

<style scoped>
p{
  font-size: 30px;
}
</style>


这个案例主要作用是帮助我们回顾下watch 的基本用法。案例中可以看到,当number变化时且为双数时,会显示hello.

通过这个小案例我们回顾了watch 的用法, 那watch 有哪些特点呢。下面就来介绍下。

一. 不能监听简单的单一数值,若要监听单一数值,只能使用getter 函数。

有的同学可能会说,你上面不是监听了不是简单数值吗?其实不是的,监听的是一个对象。用ref包裹的东西都是一个对象了。

来看一个案例:

import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗'
})

watch(data.title, (newVal, oldVal) => {
  console.log('title:' + newVal)
})

setTimeout(() => {
  data.value.title = '万里长空,天气真新鲜'
}, 2000)

这个案例中我们想监听title 的变化,然后打印变化后的title, 但是并没有如我们所愿, watch 里面的回调一直没执行。这也就验证了watch 不能监听简单单一的数值。如果我们就是想监听该怎么办呢?方法一:直接监听data.value对象,

import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
})

watch(data.value, (newVal, oldVal) => {
  console.log('title:', newVal.title)
})

setTimeout(() => {
  data.value.title = '万里长空,天气真新鲜'
}, 2000)

查看控制台:

title: 万里长空,天气真新鲜

方法二:使用getter 函数

import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
})

watch(() => data.value.title, (newVal, oldVal) => {
  console.log('title:', newVal)
})

setTimeout(() => {
  data.value.title = '万里长空,天气真新鲜'
}, 2000)

这时我们来到控制台查看,也能输出最新的标题:

title: 万里长空,天气真新鲜

二.不使用getter 函数的时候,监听是深度监听的

import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
  author: {
    gender: '男'
  }
})

watch(data.value, (newVal, oldVal) => {
  console.log('作者是:' + newVal.author.gender + '生')
})

setTimeout(() => {
  data.value.author.gender = '女'
}, 2000)

这个案例我们修改了对象的第二层,修别选项, 依然被监听到了,控制台打印出了:

作者是:女生

三. getter函数默认是浅层监听,深度监听需要修改第三个参数配置

import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
  author: {
    gender: '男'
  }
})

watch(() => data.value, (newVal, oldVal) => {
  console.log('作者是:' + newVal.author.gender + '生')
})

setTimeout(() => {
  data.value.author.gender = '女'
}, 2000)

这个案例中, watch 的回调函数永远不会执行,除非给data.value 重新赋值,比如:

setTimeout(() => {
  data.value = {
    title: '今天天气真不错',
    content: '是啊, 出去玩一下呗',
    author: {
      gender: '女'
    }
  }
}, 2000)

做了上面的修改之后, 就能在控制台打印出:

作者是:女生

getter 深度监听配置

import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
  author: {
    gender: '男'
  }
})

watch(() => data.value, (newVal, oldVal) => {
  console.log('作者是:' + newVal.author.gender + '生')
}, {
  deep: true
})

setTimeout(() => {
  data.value.author.gender = '女'
}, 2000)

核心代码:

{
  deep: true
}

查看控制台:

作者是:女生

五. watch 回调默在组件更新之前执行,若要想在组件更新之后执行需要更改第三个参数配置

<script setup>
import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
  author: {
    gender: '男'
  }
})
const titleDom = ref(null)
watch(() => data.value.title, (newVal, oldVal) => {
  console.log(titleDom.value.innerHTML)
})

setTimeout(() => {
  data.value.title = '万里长空,天气真好'
}, 2000)
</script>

<template>
  <h1 ref="titleDom">{{ data.title }}</h1>
  <p>{{ data.content }}</p>
</template>

<style scoped>
p{
  font-size: 30px;
}
</style>

这个案例中我们用ref 获取innerHTML的值, 并在watch 监听title 变化后打印, 发现打印出来的值是变化之前的值,这就验证了watch 回调默认是在组件更新之前执行的。如果想组件更新之后再执行怎么做呢?

第三个参数加入 flush: 'post' 配置

<script setup>
import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
  author: {
    gender: '男'
  }
})
const titleDom = ref(null)
watch(() => data.value.title, (newVal, oldVal) => {
  console.log(titleDom.value.innerHTML)
}, {
  flush: 'post'
})

setTimeout(() => {
  data.value.title = '万里长空,天气真好'
}, 2000)
</script>

<template>
  <h1 ref="titleDom">{{ data.title }}</h1>
  <p>{{ data.content }}</p>
</template>

<style scoped>
p{
  font-size: 30px;
}
</style>

这个时候空控制台就能打印出变化后的值:

万里长空,天气真好

六. watch 默认只有数据变化的时候才执行,若要想初始化执行则需要修改第三个参数配置

<script setup>
import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
  author: {
    gender: '男'
  }
})
const titleDom = ref(null)
watch(() => data.value.title, (newVal, oldVal) => {
  console.log(titleDom.value.innerHTML)
})
</script>

<template>
  <h1 ref="titleDom">{{ data.title }}</h1>
  <p>{{ data.content }}</p>
</template>

<style scoped>
p{
  font-size: 30px;
}
</style>

上面的代码,watch 回调永远不会执行。因为数据没有发生变化。若要想初始执行一次,第三个参数添加immediate: true

<script setup>
import { ref, watch } from 'vue'
const data = ref({
  title: '今天天气真不错',
  content: '是啊, 出去玩一下呗',
  author: {
    gender: '男'
  }
})
watch(() => data.value.title, (newVal, oldVal) => {
  console.log(newVal, oldVal)
}, {
  immediate: true
})
</script>

<template>
  <h1>{{ data.title }}</h1>
  <p>{{ data.content }}</p>
</template>

<style scoped>
p{
  font-size: 30px;
}
</style>


上面的代码, watch 回调函数中会打印: 今天天气真不错 undefined

以上就是watch 的六大特点,感谢收看,一起学习,一起进步