百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

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

ztj100 2024-11-26 11:16 13 浏览 0 评论

前言

在 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 中,当响应式状态发生变化时,都会触发一个回调函数。

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

<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和计算属性

<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函数

<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声明的响应式对象

<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 对象中的某个属性时,又会是什么情况?

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

<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 方法。

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

<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 呢?

<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 属性试试。

<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声明的响应式对象

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

<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 写法,也可以改成下面这种:

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


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

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

<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 属性。

<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 还可以侦听数组,前提是这个数组内部含有响应式数据。

<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 的回调函数的。

<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 } 即可。

<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 } 即可。

<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 是更新前的还是更新后的?

<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'。

<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 了。


作者:前端小码哥
链接:https://juejin.cn/post/7293789260581634089

相关推荐

从IDEA开始,迈进GO语言之门(idea got)

前言笔者在学习GO语言编程的时候,GO语言在国内还没有像JAVA/Php/Python那样普及,绕了不少的弯路,要开始入门学习一门编程语言,最好就先从选择一个好的编程语言的开发环境开始,有了这个开发环...

基于SpringBoot+MyBatis的私人影院java网上购票jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于SpringBoot...

基于springboot的个人服装管理系统java网上商城jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...

基于springboot的美食网站Java食品销售jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍基于springboot...

贸易管理进销存springboot云管货管账分析java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目描述贸易管理进销存spring...

SpringBoot+VUE员工信息管理系统Java人员管理jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目,JavaEEJSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。一、项目介绍SpringBoot+V...

目前见过最牛的一个SpringBoot商城项目(附源码)还有人没用过吗

帮粉丝找了一个基于SpringBoot的天猫商城项目,快速部署运行,所用技术:MySQL,Druid,Log4j2,Maven,Echarts,Bootstrap...免费给大家分享出来前台演示...

SpringBoot+Mysql实现的手机商城附带源码演示导入视频

今天为大家带来的是基于SpringBoot+JPA+Thymeleaf框架的手机商城管理系统,商城系统分为前台和后台、前台用的是Bootstrap框架后台用的是SpringBoot+JPA都是现在主...

全网首发!马士兵内部共享—1658页《Java面试突击核心讲》

又是一年一度的“金九银十”秋招大热门,为助力广大程序员朋友“面试造火箭”,小编今天给大家分享的便是这份马士兵内部的面试神技——1658页《Java面试突击核心讲》!...

SpringBoot数据库操作的应用(springboot与数据库交互)

1.JDBC+HikariDataSource...

SpringBoot 整合 Flink 实时同步 MySQL

1、需求在Flink发布SpringBoot打包的jar包能够实时同步MySQL表,做到原表进行新增、修改、删除的时候目标表都能对应同步。...

SpringBoot + Mybatis + Shiro + mysql + redis智能平台源码分享

后端技术栈基于SpringBoot+Mybatis+Shiro+mysql+redis构建的智慧云智能教育平台基于数据驱动视图的理念封装element-ui,即使没有vue的使...

Springboot+Mysql舞蹈课程在线预约系统源码附带视频运行教程

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的Springboot+Mysql舞蹈课程在线预约系统,系统项目源代码在【猿来入此】获取!https://www.yuan...

SpringBoot+Mysql在线众筹系统源码+讲解视频+开发文档(参考论文

今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的在线众筹管理系统,主要实现了普通用户在线参与众筹基本操作流程的全部功能,系统分普通用户、超级管理员等角色,除基础脚手架外...

Docker一键部署 SpringBoot 应用的方法,贼快贼好用

这两天发现个Gradle插件,支持一键打包、推送Docker镜像。今天我们来讲讲这个插件,希望对大家有所帮助!GradleDockerPlugin简介...

取消回复欢迎 发表评论: