彻底搞懂Vue3中的watch函数,功能太强了!

前言

在 Vue2 和 Vue3 中,侦听器 watch 的用法有些许不同,对于刚从 Vue2 转为 Vue3 的小伙伴来说,可能还不太适应。在本文中,我将详细为大家介绍 Vue3 中 watch 的用法和具体作用。

watch API介绍

watch(source, cb, options)

watch 共接收3个参数,下面一起看看这3个参数都有什么作用:

  • source:需要侦听的响应式属性,这个属性可以是不同形式的"数据源",例如:可以是一个 ref (包括计算属性)、可以是一个响应式对象、可以是一个 getter 函数、或多个数据源组成的数组。

  • cb:回调函数。当侦听的响应式属性发生变化时,会触发这个回调函数,它也有3个参数:newValue:响应式属性变化后的值(新值)、oldValue:响应式属性变化前的值(旧值)、onInvalidate:该函数用于清除副作用。

  • options:

    • immediate:是否在页面进入时就触发侦听器,值是一个布尔类型 true/false(默认false)。
    • deep:是否开启深层侦听。值是一个布尔类型 true/false(默认false)。
    • flush:值有3个,'pre' | 'post' | 'sync'(默认是 pre)。pre:指定的回调应该在渲染前被调用、post:可以用来将回调推迟到渲染之后的。如果回调需要通过 $refs 访问更新的 DOM 或子组件,那么则使用该值、sync:如果值设置为 sync,一旦值发生了变化,回调将被同步调用(少用,影响性能)。

基本使用

在 Vue3 中,组合式 API 中的 watch 的作用和 Vue2 中选项式 API 的 watch 作用是一样的,它们都是用来侦听响应式状态的变化。无论是在 Vue2 还是 Vue3 中,当响应式状态发生变化时,都会触发一个回调函数。

下面我们一起来看一个简单的例子:

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref 和 watch API
  import { ref, watch } from 'vue'

  // 使用响应式 ref 定义一个变量 count,初始值为 1
  let count = ref(1)

  // 定义一个方法 changeCount,该方法用于按钮点击时更改 count 的值
  const changeCount = () => {
    count.value++
  }

  // 使用 watch 函数侦听响应式 count 值的变化,并在控制台中输出修改前后的值
  watch(count, (newValue, oldValue) => {
    console.log(`新值:${newValue}`)
    console.log(`旧值:${oldValue}`)
  })
</script>

<template>
  <div id="home">
    <p>count值:{{ count }}</p>
    <button @click="changeCount">修改count值</button>
  </div>
</template>

上面这段代码中:

  • 我们使用响应式 ref 定义了一个变量 count,其初始值为 1,然后定义了一个方法 changeCount,该方法用于按钮点击时更改 count 的值。
  • 然后使用 watch 函数侦听响应式 count 变量值的变化,当 count 变量的值发生变化时,watch 回调函数将被执行,并将新值和旧值作为参数传递给该函数。
  • 当我们点击按钮时,控制台会输出如下的内容。

watch侦听器数据源类型

前面我们提到过,watch 的第一个参数 source 可以是不同形式的响应式"数据源":它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组。

那么,当我们侦听的数据源不是响应式的数据时,控制台会抛出如下图所示的警告:

侦听ref和计算属性

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref、watch 和 computed API
  import { ref, watch, computed } from 'vue'

  // 使用 ref 定义一个变量 count,初始值为 1
  let count = ref(1)
  
  // 使用 computed 定义一个计算属性 rideCount,返回 count 之间的乘积
  let rideCount = computed(() => {
    return count.value * count.value
  })
  
  // 定义一个方法 changeCount,改方法用于按钮点击时更改 count 的值,当 count 的值发生变化时,计算属性 rideCount 的值也会重新计算得到新的结果
  const changeCount = () => {
    count.value++
  }
  
  // 使用 watch 函数侦听响应式 count 值的变化
  watch(count, (newValue, oldValue) => {
    console.log(`count新值:${newValue}`, `count旧值:${oldValue}`)
  })
  
  // 使用 watch 函数侦听计算属性 rideCount 值的变化
  watch(rideCount, (newValue, oldValue) => {
    console.log(`rideCount新值:${newValue}`, `rideCount旧值:${oldValue}`)
  })
</script>

<template>
  <div id="home">
    <p>count值:{{ count }}</p>
    <p>rideCount值:{{ rideCount }}</p>
    <button @click="changeCount">修改count值</button>
  </div>
</template>

上面这段代码中:

  • 我们使用响应式 ref 定义了一个变量 count,其初始值为 1,然后使用 computed 定义一个计算属性 rideCount,返回两个 count 之间的乘积。
  • 接着我们定义了一个方法 changeCount,该方法用于按钮点击时更改 count 的值。当 count 值发生变化时,计算属性 rideCount 的值也会重新计算得到新的结果。
  • 然后使用 watch 函数侦听响应式 count 变量值和计算属性 rideCount 值的变化,当我们点击按钮时,count 值会依次加 1,当 count 变量的值发生变化时,watch 回调函数将被执行,同时也可以侦听到计算属性的变化。

侦听getter函数

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref 和 watch API
  import { ref, watch } from 'vue'

  // 使用 ref 定义两个变量 num1 和 num2,初始值都为 0
  let num1 = ref(0)
  let num2 = ref(0)
  
  // 定义两个方法 changeNum1 和 changeNum2
  // 点击"修改num1的值"按钮时,num1 的值加 5,点击"修改num2的值"按钮时,num2 的值加 10
  const changeNum1 = () => {
    num1.value += 5
  }
  
  const changeNum2 = () => {
    num2.value += 10
  }
  
  // 使用 watch 函数侦听 getter 函数的变化
  watch(
    () => num1.value + num2.value,
    (newValue, oldValue) => {
      console.log(`两数之和新:${newValue}`, `两数之和旧:${oldValue}`)
    }
  )

</script>

<template>
  <div id="home">
    <p>num1: {{ num1 }}</p>
    <p>num2:{{ num2 }}</p>
    <button @click="changeNum1">修改num1的值</button>
    <button @click="changeNum2">修改num2的值</button>
  </div>
</template>

上面这段代码中:

  • 我们使用 ref 定义两个变量 num1 和 num2,初始值都为 0
  • 然后定义了两个方法 changeNum1 和 changeNum2,当点击"修改num1的值"按钮时,num1 的值加 5,当点击"修改num2的值"按钮时,num2 的值加 10
  • 然后使用 watch 函数侦听 getter 函数的变化,getter 函数返回的是 num1 和 num2 相加的值,当 num1 和 num2 两个值其中一个发生变化时,都会执行 watch 侦听器中的回调函数。

侦听响应式对象

我们都知道,在 Vue3 中定义响应式对象的数据可以用 ref 或 reactive,那么,在使用 watch 侦听响应式对象时,这里就会有两种情况:

侦听reactive声明的响应式对象

typescript 复制代码
<script setup>
  // 引入 vue 提供的 reactive 和 watch API
  import { reactive, watch } from 'vue'

  // 使用 reactive 定义一个响应式对象 person
  let person = reactive({
    name: 'Echo',
    address: {
      city: 'GuangZhou'
    }
  })
  
  // 定义一个方法 changeName,用于更改 person 对象中的 name 属性值
  const changeName = () => {
    person.name = 'Steven'
  }
  
  // 定义一个方法 changeCity,用于更改 person 对象中,address 对象中的 city 属性值
  const changeCity = () => {
    person.address.city = 'San Francisco'
  }
  
  // 使用 watch 函数侦听响应式对象 person 的变化
  watch(person, (newValue, oldValue) => {
    console.log(newValue, oldValue)
  })

</script>

<template>
  <div id="home">
    <p>name: {{ person.name }}</p>
    <p>city: {{ person.address.city }}</p>
    <button @click="changeName">修改name</button>
    <button @click="changeCity">修改city</button>
  </div>
</template>

上面这段代码中:

  • 我们使用 reactive 定义一个响应式对象 person,里面有 name 和 address 属性,其中 address 是一个对象,包含了 city 属性。
  • 然后定义了两个方法 changeName 和 changeCity,changeName 方法用于更改 person 对象中的 name 属性值, changeCity 方法用于更改 person 对象中,address 对象中的 city 属性值。
  • 然后使用 watch 函数侦听响应式对象 person 的变化,当我们不管是点击"修改name"还是"修改city"按钮时,都会执行 watch 侦听器中的回调函数。

从上面可以看到:不管是 name 还是 city 的值发生变化,都会触发 watch 函数,因此我们可以得出两个结论:

  • 当我们侦听的是用 reactive 声明的响应式对象时,修改响应式对象的任何属性,都会触发 watch 函数。
  • 当侦听的响应式数据是 Proxy 类型时,deep 配置项无效,无论设置成 true 还是 false,都会进行深度监听。

然而我们发现有一个问题,在控制台中打印出来的新旧值是一样的,这是什么原因?

这是由于我们定义的 person 数据是通过响应是 reactive 定义的,reactive返回的数据是 Proxy 类型的,由于 newValue 和 oldValue 是同一个地址引用,所以属性值也是一样的。

上面我们侦听的是由 reactive 定义的 person 对象,也就是 Proxy 对象,当我们监听的是 Proxy 对象中的某个属性时,又会是什么情况?

首先,当侦听的属性是简单数据类型时:

typescript 复制代码
<script setup>
  // 引入 vue 提供的 reactive 和 watch API
  import { reactive, watch } from 'vue'

  // 使用 reactive 定义一个响应式对象 person
  let person = reactive({
    name: 'Echo',
    address: {
      city: 'GuangZhou'
    }
  })
  
  // 定义一个方法 changeName,
  const changeName = () => {
    person.name = 'Steven'
  }
  
  // 定义一个方法 changeCity
  const changeCity = () => {
    person.address.city = 'San Francisco'
  }
  
  // 使用 watch 函数侦听 name 属性的变化
  watch(
    () => person.name,
    (newValue, oldValue) => {
      console.log(`新name:${newValue}`, `旧name:${oldValue}`)
    }
  )

</script>

<template>
  <div id="home">
    <p>name: {{ person.name }}</p>
    <p>city: {{ person.address.city }}</p>
    <button @click="changeName">修改name</button>
    <button @click="changeCity">修改city</button>
  </div>
</template>

上面这段代码中:

  • 我们使用 watch 侦听器侦听 person 对象中的 name 属性。name 属于简单数据类型。
  • 当我们点击"修改name"按钮时,newValue 的值为"steven",oldValue 的值为 "Echo",当我们点击"修改city"按钮时,不会再触发 watch 函数。

需要注意的是:此时的 watch 函数中的第一个参数应当是一个箭头函数。

因此我们可以得出一个结论:

当我们侦听响应式对象中的某个属性时,只有当响应式对象的被侦听属性发生变化时,才会触发 watch 方法,其他属性变化不会触发 watch 方法。

当侦听的属性为复杂数据类型时:

typescript 复制代码
<script setup>
  // 引入 vue 提供的 reactive 和 watch API
  import { reactive, watch } from 'vue'

  // 使用 reactive 定义一个响应式对象 person
  let person = reactive({
    name: 'Echo',
    address: {
      city: 'GuangZhou'
    }
  })
  
  // 定义一个方法 changeName,
  const changeName = () => {
    person.name = 'Steven'
  }
  
  // 定义一个方法 changeCity
  const changeCity = () => {
    person.address.city = 'San Francisco'
  }
  
  // 使用 watch 函数侦听 address 属性的变化
  watch(
    () => person.address,
    (newValue, oldValue) => {
      console.log(`新name:${newValue}`, `旧name:${oldValue}`)
    }
  )

</script>

<template>
  <div id="home">
    <p>name: {{ person.name }}</p>
    <p>city: {{ person.address.city }}</p>
    <button @click="changeName">修改name</button>
    <button @click="changeCity">修改city</button>
  </div>
</template>

上面这段代码中:

  • 我们使用 watch 侦听器侦听 person 对象中的 address 对象,address 属于复杂数据类型。
  • 当我们点击"修改name"或者点击"修改city"按钮时,我们可以看到页面的值被更改了,但控制台一次都不输出。

我们不妨设想一下,改 address 里面的 city 属性,不会触发 watch 函数,那直接更改 address 呢?

typescript 复制代码
<script setup>
  // 引入 vue 提供的 reactive 和 watch API
  import { reactive, watch } from 'vue'

  // 使用 reactive 定义一个响应式对象 person
  let person = reactive({
    name: 'Echo',
    address: {
      city: 'GuangZhou'
    }
  })
  
  // 定义一个方法 changeName,
  const changeName = () => {
    person.name = 'Steven'
  }
  
  // 定义一个方法 changeCity
  const changeCity = () => {
    person.address = {
      city: 'ShangHai'
    }
  }
  
  // 使用 watch 函数侦听 address 属性的变化
  watch(
    () => person.address,
    (newValue, oldValue) => {
      console.log(`新:${JSON.stringify(newValue)}`)
      console.log(`旧:${JSON.stringify(oldValue)}`)
    }
  )

</script>

<template>
  <div id="home">
    <p>name: {{ person.name }}</p>
    <p>city: {{ person.address.city }}</p>
    <button @click="changeName">修改name</button>
    <button @click="changeCity">修改city</button>
  </div>
</template>

上面这段代码中:

  • 我们在 changeCity 方法中直接更改 address 属性的值。
  • 可以看到,点击"修改city"按钮时,控制台会打印。

这里我们可以回想到,在 Vue2 中,侦听引用类型的数据时,需要深度侦听,Vue3也应该如此,所以我们给 watch 函数的第三个参数加个 deep: true 属性试试。

typescript 复制代码
<script setup>
  // 引入 vue 提供的 reactive 和 watch API
  import { reactive, watch } from 'vue'

  // 使用 reactive 定义一个响应式对象 person
  let person = reactive({
    name: 'Echo',
    address: {
      city: 'GuangZhou'
    }
  })
  
  // 定义一个方法 changeCity
  const changeCity = () => {
    person.address.city = 'San Francisco'
  }
  
  // 定义一个方法 changeAddress
  const changeAddress = () => {
    person.address = {
      city: 'ShangHai'
    }
  }
  
  // 使用 watch 函数侦听 address 属性的变化
  watch(
    () => person.address,
    (newValue, oldValue) => {
      console.log(`新:${JSON.stringify(newValue)}`)
      console.log(`旧:${JSON.stringify(oldValue)}`)
    },
    {
      deep: true
    }
  )

</script>

<template>
  <div id="home">
    <p>city: {{ person.address.city }}</p>
    <button @click="changeCity">修改city</button>
    <button @click="changeAddress">修改address</button>
  </div>
</template>

上面代码中:

  • 我们给 watch 函数的第三个参数加了 deep: true 属性。
  • 此时,我们不管是点击"修改city"按钮,还是点击"修改address"按钮,都会在控制台打印输出。

这里我们可以总结出一个结论:

当我们侦听的属性类型是复杂的数据类型时,需要修改属性本身,才能触发 watch 侦听,如果要修改更深层的属性,需要将 deep 设置为 true 才能进行深度监听。

侦听ref声明的响应式对象

首先,当侦听的属性类型是简单数据类型时:

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref 和 watch API
  import { ref, watch } from 'vue'

  // 使用 ref 定义一个响应式属性 count,初始值为 0
  let count = ref(0)
  
  // 定义一个方法 changeCount,用于修改 count 值
  const changeCount = () => {
    count.value++
  }
  
  // 使用 watch 函数侦听 count 属性的变化
  watch(count, (newValue, oldValue) => {
    console.log(`新count值:${newValue},`, `旧count值:${oldValue}`)
  })

</script>

  <template>
    <div id="home">
      <p>count: {{ count }}</p>
    	<button @click="changeCount">修改Count</button>
    </div>
  </template>

上面这段代码中:

  • 我们使用响应式 ref 定义了一个变量 count,其初始值为 0,然后定义了一个方法 changeCount,该方法用于按钮点击时更改 count 的值。
  • 然后使用 watch 函数侦听响应式 count 变量值的变化,当 count 变量的值发生变化时,watch 回调函数将被执行,并将新值和旧值作为参数传递给该函数。
  • 当我们点击按钮时,控制台会输出如下的内容。

上面的 watch 写法,也可以改成下面这种:

typescript 复制代码
// 使用 watch 函数侦听 count 属性的变化
watch(
  () => count.value,
  (newValue, oldValue) => {
    console.log(`新count值:${newValue},`, `旧count值:${oldValue}`)
  }
)

当侦听的数据类型是复杂数据类型时:

当我们使用 ref 声明复杂数据类型时,内部会使用 reactive 将数据转化为 Proxy 类型。

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref 和 watch API
  import { ref, watch } from 'vue'

  // 使用 ref 定义一个响应式对象 person
  let person = ref({
    name: 'Echo',
    address: {
      city: 'GuangZhou'
    }
  })
  
  // 定义一个方法 changePerson
  const changePerson = () => {
    person.value = {
      name: 'Steven',
      address: {
        city: 'San Francisco'
      }
    }
  }
  
  // 定义一个方法 changeCity
  const changeCity = () => {
    person.value.address.city = 'ShangHai'
  }
  
  // 使用 watch 函数侦听 address 属性的变化
  watch(person, (newValue, oldValue) => {
    console.log(newValue, oldValue)
  })

</script>

<template>
  <div id="home">
    <p>city: {{ person.address.city }}</p>
    <button @click="changePerson">修改person</button>
    <button @click="changeCity">修改city</button>
  </div>
</template>

上面这段代码中:

  • 我们使用 watch 函数侦听 person 属性的变化,当我们点击"修改person"按钮时,会触发 watch 函数,而点击"修改city"按钮时,不会触发。

下面,我们给 watch 函数的第三个参数加个 deep: true 属性。

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref 和 watch API
  import { ref, watch } from 'vue'

  // 使用 ref 定义一个响应式对象 person
  let person = ref({
    name: 'Echo',
    address: {
      city: 'GuangZhou'
    }
  })
  
  // 定义一个方法 changePerson
  const changePerson = () => {
    person.value = {
      name: 'Steven',
      address: {
        city: 'San Francisco'
      }
    }
  }
  
  // 定义一个方法 changeCity
  const changeCity = () => {
    person.value.address.city = 'ShangHai'
  }
  
  // 使用 watch 函数侦听 address 属性的变化
  watch(
    person,
    (newValue, oldValue) => {
      console.log(newValue, oldValue)
    },
    {
      deep: true
    }
  )

</script>

<template>
  <div id="home">
    <p>city: {{ person.address.city }}</p>
    <button @click="changePerson">修改person</button>
    <button @click="changeCity">修改city</button>
  </div>
</template>

此时,我们可以看到,不管是点击哪个按钮,都可以触发 watch 函数。

因此,我们也可以得出一个结论:

当我们侦听的属性类型是复杂的数据类型时,如果要修改更深层的属性,需要将 deep 设置为 true 才能进行深度监听。

侦听多个来源组成的数组

watch 还可以侦听数组,前提是这个数组内部含有响应式数据。

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref、reactive 和 watch API
  import { ref, reactive, watch } from 'vue'

  let a = ref(0)
  let b = reactive({ num: 0 })
  
  // 使用 watch 函数侦听多个来源的数组
  watch(
    [a, () => b.num],
    ([newA, newB], [oldA, oldB]) => {
      console.log(`a的值新:${newA}`, `a的值旧:${oldA}`)
      console.log(`b的值新:${newB}`, `b的值旧:${oldB}`)
    }
  )
  
  // 定义一个方法 changeA,修改 a 的值
  const changeA = () => {
    a.value += 10
  }
  
  // 定义一个方法 changeB,修改 b 的值
  const changeB = () => {
    b.num = 20;
  }
</script>

<template>
  <div id="home">
    <p>a: {{ a }}</p>
    <p>b: {{ b.num }}</p>
    <button @click="changeA">修改A</button>
    <button @click="changeB">修改B</button>
  </div>
</template>

上面这段代码中:

  • 我们定义了两个变量 a 和 b,其中 a 是通过 ref 定义的,b 是通过 reactive 定义的。
  • 然后使用 watch 侦听 a 和 b.num 的值,当 a 的值或者 b 的值发生变化时,都会触发 watch 函数。

深层侦听器

我们在前面的代码中有提到过,如果我们使用 watch 函数侦听一个响应式对象时,只要对象里面的某个属性发生了变化,那么就会执行侦听器回调函数。

原因是因为直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器。

但是,如果我们是使用的 getter 函数返回响应式对象的形式,如果不添加深层侦听器,那么响应式对象的属性值发生变化时,是不会触发 watch 的回调函数的。

typescript 复制代码
<script setup>
  // 引入 vue 提供的 reactive 和 watch API
  import { reactive, watch } from 'vue'

  let count = reactive({ number: 0 })
  
  const changeCountNumber = () => {
    count.number++
  }
  
  watch(
    () => count,
    (newValue, oldValue) => {
      console.log(newValue, oldValue)
    }
  )

</script>

<template>
  <div id="home">
    <p>count的number值: {{ count.number }}</p>
  	<button @click="changeCountNumber">修改number</button>
  </div>
</template>

上面这段代码中:

  • 我们使用 reactive 定义了一个响应式对象 count,然后定义了一个方法 changeCountNumber,该方法主要用于改变响应式对象 count 中 number 的值。
  • 然后使用 watch 函数侦听响应式对象,其中数据源用 getter 函数返回了响应式对象,当我们更改 count 中 number 的值时,watch 的回调函数是不会执行的。

为了实现上述代码的侦听,我们可以手动给侦听器加上深层侦听的效果。

添加深层侦听很简单,我们只需要给 watch 函数添加第三个参数 { deep: true } 即可。

typescript 复制代码
<script setup>
  // 引入 vue 提供的 reactive 和 watch API
  import { reactive, watch } from 'vue'

  let count = reactive({ number: 0 })
  
  const changeCountNumber = () => {
    count.number++
  }
  
  watch(
    () => count,
    (newValue, oldValue) => {
      console.log(newValue, oldValue)
    },
    {
      deep: true
    }
  )

</script>

<template>
  <div id="home">
    <p>count的number值: {{ count.number }}</p>
    <button @click="changeCountNumber">修改number</button>
  </div>
</template>

上面这段代码中:

  • 我们给 watch 添加深层侦听,当响应式对象 count 中的 number 值发生变化时,会触发 watch 函数。
  • 此时我们可以看到 newValue 和 oldValue 的值是相等的,除非我们把响应式对象即 count 整个替换掉,那么这两个值才会变得不一样。

需要注意的是:深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。

即时回调的侦听器

watch 默认是懒执行的:仅当数据源发生变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。

我们只需要给 watch 函数添加第三个参数 { immediate: true } 即可。

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref 和 watch API
  import { ref, watch } from 'vue'

  let count = ref(0)
  
  const changeCount = () => {
    count.value++
  }
  
  watch(
    count,
    (newValue, oldValue) => {
      console.log(`新的count:${newValue},旧的count:${oldValue}`)
    },
    {
      immediate: true
    }
  )

</script>

<template>
  <div id="home">
    <p>count值: {{ count }}</p>
  	<button @click="changeCount">修改count</button>
  </div>
</template>

上面这段代码中:

  • 我们给 watch 函数添加第三个参数 { immediate: true }。
  • 在第一次进入页面时,会先调用一次 watch 回调函数,然后当 count 值发生变化时,会再次触发 watch 函数执行回调。

回调的触发时机

大家思考一个问题:如果我们在侦听器的回调函数中来获取 DOM,此时我们获取到的这个 DOM 是更新前的还是更新后的?

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref 和 watch API
  import { ref, watch } from 'vue'

  let name = ref('张三')
  const nameRef = ref();
  
  const changeName = () => {
    name.value = '李四'
  }
  
  watch(
    name,
    (newValue, oldValue) => {
      console.log(`新的name:${newValue},旧的name:${oldValue}`)
      console.log(`DOM 节点:${nameRef.value.innerHTML}`)
    }
  )

</script>

<template>
  <div id="home">
    <p ref="nameRef">name: {{ name }}</p>
    <button @click="changeName">修改name</button>
  </div>
</template>

上面这段代码中:

  • 我们通过点击按钮更改 name 的值,把"张三"修改成"李四"。
  • 但是我们发现在侦听器的回调函数里面获取到的 DOM 节点里面的内容还是"张三",说明在侦听器回调中访问的 DOM 是 Vue 更新之前的状态。

如果想在侦听器回调中访问 Vue 更新之后的 DOM,我们只需要再给侦听器多传递一个参数选项即可:flush: 'post'。

typescript 复制代码
<script setup>
  // 引入 vue 提供的 ref 和 watch API
  import { ref, watch } from 'vue'

  let name = ref('张三')
  const nameRef = ref();
  
  const changeName = () => {
    name.value = '李四'
  }
  
  watch(
    name,
    (newValue, oldValue) => {
      console.log(`新的name:${newValue},旧的name:${oldValue}`)
      console.log(`DOM 节点:${nameRef.value.innerHTML}`)
    },
    {
      flush: 'post'
    }
  )

</script>

<template>
  <div id="home">
    <p ref="nameRef">name: {{ name }}</p>
    <button @click="changeName">修改name</button>
  </div>
</template>

此时,我们可以看到,获取到的 DOM 就是 Vue 更新之后的 DOM 了。

总结

以上就是我对 Vue3.0 中 watch 函数的了解,希望可以帮助正在学习 Vue3.0 的朋友!

如果有不正确的地方,欢迎大家在评论区多多指正!

看完记得点个赞哦~ 谢谢!🤞🤞🤞

相关推荐
Json____4 分钟前
学法减分交管12123模拟练习小程序源码前端和后端和搭建教程
前端·后端·学习·小程序·uni-app·学法减分·驾考题库
迂 幵12 分钟前
vue el-table 超出隐藏移入弹窗显示
javascript·vue.js·elementui
上趣工作室16 分钟前
vue2在el-dialog打开的时候使该el-dialog中的某个输入框获得焦点方法总结
前端·javascript·vue.js
家里有只小肥猫17 分钟前
el-tree 父节点隐藏
前端·javascript·vue.js
fkalis18 分钟前
【海外SRC漏洞挖掘】谷歌语法发现XSS+Waf Bypass
前端·xss
陈随易1 小时前
农村程序员-关于小孩教育的思考
前端·后端·程序员
云深时现月1 小时前
jenkins使用cli发行uni-app到h5
前端·uni-app·jenkins
昨天今天明天好多天1 小时前
【Node.js]
前端·node.js
2401_857610032 小时前
深入探索React合成事件(SyntheticEvent):跨浏览器的事件处理利器
前端·javascript·react.js
_xaboy2 小时前
开源项目低代码表单设计器FcDesigner扩展自定义的容器组件.例如col
vue.js·低代码·开源·动态表单·formcreate·低代码表单·可视化表单设计器