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

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

wxin55 2024-10-31 15:40 11 浏览 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

相关推荐

ES6中 Promise的使用场景?(es6promise用法例子)

一、介绍Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码doSomething(f...

JavaScript 对 Promise 并发的处理方法

Promise对象代表一个未来的值,它有三种状态:pending待定,这是Promise的初始状态,它可能成功,也可能失败,前途未卜fulfilled已完成,这是一种成功的状态,此时可以获取...

Promise的九大方法(promise的实例方法)

1、promise.resolv静态方法Promise.resolve(value)可以认为是newPromise方法的语法糖,比如Promise.resolve(42)可以认为是以下代码的语...

360前端一面~面试题解析(360前端开发面试题)

1.组件库按需加载怎么做的,具体打包配了什么-按需加载实现:借助打包工具(如Webpack的require.context或ES模块动态导入),在使用组件时才引入对应的代码。例如在V...

前端面试-Promise 的 finally 怎么实现的?如何在工作中使用?

Promise的finally方法是一个非常有用的工具,它无论Promise是成功(fulfilled)还是失败(rejected)都会执行,且不改变Promise的最终结果。它的实现原...

最简单手写Promise,30行代码理解Promise核心原理和发布订阅模式

看了全网手写Promise的,大部分对于新手还是比较难理解的,其中几个比较难的点:状态还未改变时通过发布订阅模式去收集事件实例化的时候通过调用构造函数里传出来的方法去修改类里面的状态,这个叫Re...

前端分享-Promise可以中途取消啦(promise可以取消吗)

传统Promise就像一台需要手动组装的设备,每次使用都要重新接线。而Promise.withResolvers的出现,相当于给开发者发了一个智能遥控器,可以随时随地控制异步操作。它解决了三大...

手写 Promise(手写输入法 中文)

前言都2020年了,Promise大家肯定都在用了,但是估计很多人对其原理还是一知半解,今天就让我们一起实现一个符合PromiseA+规范的Promise。附PromiseA+规范地址...

什么是 Promise.allSettled()!新手老手都要会?

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的pr...

前端面试-关于Promise解析与高频面试题示范

Promise是啥,直接上图:Promise就是处理异步函数的API,它可以包裹一个异步函数,在异步函数完成时抛出完成状态,让代码结束远古时无限回掉的窘境。配合async/await语法糖,可...

宇宙厂:为什么前端离不开 Promise.withResolvers() ?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.为什么需要Promise.with...

Promise 新增了一个超实用的 API!

在JavaScript的世界里,Promise一直是处理异步操作的神器。而现在,随着ES2025的发布,Promise又迎来了一个超实用的新成员——Promise.try()!这个新方法简...

一次搞懂 Promise 异步处理(promise 异步顺序执行)

PromisePromise就像这个词的表面意识一样,表示一种承诺、许诺,会在后面给出一个结果,成功或者失败。现在已经成为了主流的异步编程的操作方式,写进了标准里面。状态Promise有且仅有...

Promise 核心机制详解(promise机制的实现原理)

一、Promise的核心状态机Promise本质上是一个状态机,其行为由内部状态严格管控。每个Promise实例在创建时处于Pending(等待)状态,此时异步操作尚未完成。当异步操作成功...

javascript——Promise(js实现promise)

1.PromiseES6开始支持,Promise对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示。简单说就是处理异步请求的。之所以叫Promise,就是我承诺,如果成功则怎么处理,失败怎...

取消回复欢迎 发表评论: