第五节: 带你全面理解 vue3 中 computed, watch, watchEffect 组合式API的使用

news2024/11/18 9:36:06

前言:

上一章, 带大家分析了vue3核心响应式API中的三个, 即reactive,ref, readonly.

本章将会带大家分另外几个工作中比较常用的组合式API.

1. computed 计算属性

vue2中, 我们是通过computed选项添加计算属性的, 关于计算属性的本质, 这里就不过多阐述了, 如果还有不了解的同学, 可以去看vue2专栏中,关于computed计算属性讲解

1.1. computed 基本使用

computed组合式API, 接受一个 getter 函数作为参数,返回一个只读的响应式 ref对象。该 ref 通过 .value 暴露 getter 函数的返回值。

这句话看着有点拗口, 我们通过示例来分析computedAPI的使用.

示例:

<template>
  <div>
    <h2>computed</h2>
    <div>{{ userName }}</div>
    <button @click="change">修改依赖</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed, ref } from 'vue'

export default defineComponent({
  setup() {
    // 计算属性依赖
    const firstName = ref("张")
    const lastName = ref("三")

    // 计算属性返回的ref数据
    const userName = computed(() => {
      console.log("computed")
      return firstName.value + ' ' + lastName.value
    })
    console.log("userName", userName)

    // 修改计算属性的依赖
    const change = () => {
      firstName.value = "李"
    }

    return { userName, change }
  },
})
</script>

控制台输出结果:
在这里插入图片描述

接下来我们对代码示例进行分析.主要从以下以下几点分析:

computed 返回值分析

通过控制台输出的computedAPI 返回的结果, 你会发现, 结构与refAPI 创建响应式数据结构极度相似. 这也说明了一点, computed返回的数据也是具有响应性的, 同时使用方式也与ref数据, 通过.value属性进行操作.

computed 参数分析

让我们将目光移入computedAPI 的参数部分, 参数是一个回调函数, 这个回调函数返回一个数据, 返回的数据是有两个ref数据拼接而成. 这两个具有响应性的ref数据我们就称为是computed数据的依赖.

这个回调函数就是所谓的getter函数. 函数的返回值,就是computed返回的ref对象的value属性值.

computed参数的回调函数会在初始时自动调用一次, 后续只有当依赖项数据发生变化,才会促使参数getter函数重新执行获取最新的数据. 否则computed返回ref数据无论使用多少次, 结果都是一样的.

computed 返回数据在模板上使用

computedAPI 返回的ref数据在使用上与refAPI 创建的数据完全一致. 在模板上使用会自动解包, 因此我们不需要在模板中使用.value

示例代码中的修改逻辑

针对示例代码中修改数据的逻辑, 主要在模板上绑定了click事件, 当事件被触发时, 会执行事件处理函数, 即change函数., ,在change函数中修改了具有响应性的ref数据, 即firstName,

firstName作为计算属性computed参数getter函数 依赖项. 根据计算属性特性, 当依赖项发生变化, 会自动执行getter函数, 返回计算后最新的数据.

也就意味着userName这个具有响应性的ref数据发生了变化, 进而触发页面模板重新渲染, 更新视图

据此总结: computed 返回的数据也是具有响应性的

1.2. computed 计算属性设置

vue2中, 计算属性参数可以是一个函数, 如果是一个函数表示getter函数. 但如果参数是一个对象, 对象可以具有getter函数和setter函数.

通过vue3computedAPI 也可以接受一个带有 gettersetter 函数的对象来创建一个可写的 ref 对象。


示例:

<template>
  <div>
    <h2>computed</h2>
    <div>{{ userName }}</div>
    <button @click="change">修改依赖</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed, ref } from 'vue'

export default defineComponent({
  setup() {
    // 计算属性依赖
    const firstName = ref("张")
    const lastName = ref("三")

    // 计算属性返回的ref数据
    const userName = computed({
      get() {
        console.log('获取计算属性')
        return firstName.value + ' ' + lastName.value
      },
      set(value) {
        console.log('设置计算属性',value)
        const nameArr = value.split(' ')
        firstName.value = nameArr[0]
        lastName.value = nameArr[1]
      }

    })

    // 修改计算属性的依赖
    const change = () => {
      // firstName.value = "李"
      userName.value = '李 四'
      console.log(userName.value)
    }

    return { userName, change }
  },
})
</script>

1.3. computed 最佳使用

官网对此也有描述, computed计算属性最佳使用方式有两点:

  • 计算属性getter函数中不应该修改其他状态数据,或异步操作. getter函数的本质就是根据依赖项计算最新的结果.
  • 计算属性本身就是根据依赖项生成的快照信息, 具有一定的缓存作用, 因此修改的意义不大. 所以不建议使用setter函数.

2. watch 侦听器

watch侦听器的作用,在vue2中也分析过. 就是监听数据的变化, 当数据发生变化时处理一些事情

watch侦听器可以侦听一个多个响应式数据源,并在数据源变化时调用所给的回调函数。

2.1. watch 基本使用

侦听器watch api 接受三个参数

  1. 第一个参数: 侦听数据源,
  2. 第二个参数: 侦听数据源发生变化时执行的回调函数,回调函数接受三个参数: 新值,旧值,以及清理副作用的回调函数
  3. 第三个参数: 一个设置侦听配置对象, 为可选参数

示例:

<template>
  <div>
    <h2>watch</h2>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref,  watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)

    // 侦听响应数据变化
    watch(
      count,
      () => {
        console.log('count', count.value)
      }
    )

    // 修改监听数据源
    const change = () => {
      count.value = 100
    }
    return { change }
  },

})
</script>

示例描述:

  1. 示例中watch的第一个参数 count 为侦听数据源
  2. watch 第二个参数是一个回调函数, 当count值发生变化时执行第二个参数回调函数

2.2. watch 默认是懒侦听的

watch 侦听一个数据源, 在侦听时默认是懒侦听的, 也就是初始时不会触发侦听器的回调函数,只有当数据源发生变化时才会调用回调函数

示例:

<template>
  <div>
    <h2>watch</h2>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref,  watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)

    // 侦听响应数据变化
    watch(
      count,
      () => {
        console.log('count', count.value)
      }
    )

    // 修改监听数据源
    const change = () => {
      count.value = 100
    }
    return { change }
  },

})
</script>

通过示例会发现,初始时watch 第二个参数回调函数不会执行, 只有当count 修改时才会触发侦听器

2.3. watch 侦听数据源

watch 第一个参数是侦听器的数据源。这个数据源可以是以下几种:

  1. 一个函数,返回一个值

  2. 一个 ref

  3. 一个响应式对象

  4. 或是由以上类型的值组成的数组

侦听源为ref数据

示例:

<template>
  <div>
    <h2>watch</h2>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref,  watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)

    // 侦听响应数据变化
    watch(
      count,
      () => {
        console.log('count', count.value)
      }
    )

    // 修改监听数据源
    const change = () => {
      count.value = 100
    }
    return { change }
  },

})
</script>

示例中countref 数据, 因此当count 变化时,会触发响应,执行watch 侦听的回调函数

侦听源为reactive响应对象

示例

<template>
  <div>
    <h2>watch</h2>
    {{ user }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent,  watch, reactive } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个reactive响应数据
    const user = reactive({ name: '张三', age: 20 })

    // 侦听响应数据变化
    watch(
      user,
      () => {
        console.log('user', user)
      }
    )

    // 修改监听数据源
    const change = () => {
      user.name = '李四'
    }
    return { user, change }
  },

})
</script>

示例中, watch 侦听器的数据源是一个reactive响应式数据, 因此当数据发生变化时,watch 会执行侦听器的回调函数

侦听源是一个函数

watch 侦听数据源只能是响应对象, 如果我们项监听响应对象某个属性的变化,如果属性值是一个原始类型类型的值, 那么就会报ts就会报错

此时就需要使用函数式写法, 函数返回需要监听的数据

示例

<template>
  <div>
    <h2>watch</h2>
    {{ user }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch, reactive } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const user = reactive({ name: '张三', age: 20 })

    // 侦听源是函数写法
    watch(
      () => user.name,
      () => {
        console.log('user', user)
      }
    )

    // 修改监听数据源
    const change = () => {
      user.name = '李四'
    }
    return { user, change }
  },

})
</script>

侦听数据源是一个数组

以上三种用法都是监听一个数据的变化, 如果希望监听多个数据的变化, 可以使用数组的形式

侦听数据源可以是以上三种组成的数组

示例:

<template>
  <div>
    <h2>watch</h2>
    {{ user }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch, reactive } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)
    const user = reactive({ name: '张三', age: 20 })
    const person = reactive({ name: '小明' })

  	// 侦听多个数据源:ref, 响应对象, 函数组成的数组
    watch(
      [count, user, () => person.name],
      () => {
        console.log('监听触发了')
      }
    )

    // 修改任意一个数据源,都会触发侦听
    const change = () => {
      count.value = 100
      // user.name = '李四'
      // person.name = '李四'
    }
    return { user, change }
  },

})
</script>

2.4. watch 回调函数

watch函数的第二个参数就是回调函数, 也就是说当侦听源发生变化时,执行回调函数

此回调函数接受以下几个参数:

  1. 侦听数据源最新的值
  2. 侦听数据源变化前的旧值
  3. 清理副作用的回调函数

新旧值参数

首先来看一下新值和旧值两个参数, 这是在使用watch时比较常用到的参数

示例:

<template>
  <div>
    <h2>watch</h2>
    {{ count }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)


    // 侦听响应数据变化
    watch(
      count,
      (nv, ov) => {
        console.log('新值', nv)  // 100
        console.log('旧值', ov)  // 10
      }
    )

    // 修改监听数据源
    const change = () => {
      count.value = 100
    }
    return { count, change }
  },

})
</script>

示例代码中watch 侦听一个ref数据的变化, 当ref数据发生变化时, 执行watch 的回调函数(即watch的第二个参数)

这个回调函数接受两个参数, 第一个是ref 数据的新值, 第二个参数是侦听ref 数据的旧值

清理副作用的参数

接下来我们看一下回调函数第三个参数的使用: 清理副作用

watch 侦听的回调函数中第三个参数是清理副作用的函数, 此函数接受一个函数作为参数

示例:

<template>
  <div>
    <h2>watch</h2>
    {{ count }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)

  	// 延迟打印参数
    const printInfo = (val: any) => {
      let timer = setTimeout(() => {
        console.log('val', val)
      }, 3000)


      // 清理定时器
      const clearTimer = () => {
        clearTimeout(timer)
      }
      return {
        clearTimer
      }
    }

    
	// 侦听ref数据变化
    watch(
      count,
      (nv, ov, onCleanup) => {
        // 执行printInfo函数, 返回一个清理定时器的函数
        const { clearTimer } = printInfo(nv)

        // 清理副作用函数
        onCleanup(clearTimer)
      }
    )

   

    // 修改监听数据源
    const change = () => {
      count.value++
    }
    return { count, change }
  },

})
</script>

示例中, 当count 数据发生变化时,watch 执行回调函数, 在回调函数中, 调用printInfo 传入新值,

printInfo 函数中, 延迟三秒打印传入的count值,并返回一个用与关闭延迟定时器的函数

watch 回调函数中通过解构的方式获取到关闭定时器的函数,并作为参数传给了回调函数的第三个参数

此时当时间不满三秒时, count再次发生变化, 此时又一次调用watch 回调函数,在此回调函数中就会清理上一次的副作用, 关闭上一次的定时器


2.5. watch 选项对象

watch 第三个可选的参数是一个对象,支持以下这些选项:

  1. immediate: 在侦听器创建时立即触发回调。第一次调用时旧值是 undefined

  2. deep: 如果源是对象,强制深度遍历,以便在深层级变更时触发回调

  3. flush: 调整回调函数的刷新时机

immediate 初始执行监听函数

初始化立即执行watch侦听器的回调函数

示例:

<template>
  <div>
    <h2>watch</h2>
    {{ count }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)


    // 侦听响应数据变化
    watch(
      count,
      (nv, ov) => {
        console.log('新值', nv)  // 100
        console.log('旧值', ov)  // 10
      },
    	// 选项对象
      {
        immediate: true // 初始监听
      }
      
    )

    // 修改监听数据源
    const change = () => {
      count.value = 100
    }
    return { count, change }
  },

})
</script>

当我们没有使用immediate 选项时, 默认值为false, 此时初始侦听器回调函数不会执行, 只有当侦听源发生变化时才会触发侦听器, 调用回调函数, 新增为修改后的值, 旧值为修改之前的值

如果使用immediate 选项,组件初始化时就会执行侦听器回调函数, 此时回调函数新增为侦听源初始值, 旧值为undefined


deep 深度监听选项

deep 选项默认值为false, 如果设置为true时, 则表示深度监听, 即侦听源对象中属性的变化也会被侦听到, 执行回调函数

示例

<template>
  <div>
    <h2>watch</h2>
    {{ user }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed, ref, getCurrentInstance, watch, reactive } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const user = ref({ name: '张三', age: 20 })

    watch(
      user,
      (nv, ov,) => {
        console.log('侦听器触发了', nv, ov)
      },
      {
        deep: true
      }
    )

    // 修改监听数据源
    const change = () => {
      user.value.name = '李四'
    }
    return { user, change }
  },

})
</script>

示例中如果没有添加deep选项时, 修改ref 数据中name属性不会触发侦听器, 只有整体修改时才会触发侦听器, 如下:

user.value = { name: "李四", age: 28 }

当使用deep 选项, 值设置为true时, 表示深度监听, 此时通过如下修改依然可以触发侦听器

user.value.name = '李四'

flush: 调整回调函数的刷新时机

当你更改了响应式状态,它可能会同时触发 Vue 组件更新(视图更新)和侦听器回调。

默认情况下,侦听器回调,都会在 Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。

示例:

<template>
  <div>
    <h2>watch</h2>
    <div ref="userRef"> {{ user }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch, reactive } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const user = reactive({ name: '张三', age: 20 })

    // 获取dom节点
    const userRef = ref()

    watch(
      user,
      (nv, ov,) => {
        console.log('侦听器触发了', nv, ov)
        console.log('获取dom节点', userRef.value)
      },
    )


    // 修改监听数据源
    const change = () => {
      user.name = '李四'
    }
    return { user, change, userRef }
  },

})
</script>

控制台输出效果:
在这里插入图片描述

通过示例的运行结果, 可以很明确的看到:

修改数据时,watch 监听的回调函数已经执行, 但是组件视图并没有更新,因此获取的dom节点显示的依然是之前的内容,

侦听器回调函数执行完毕后,才会执行组件视图的更新, 你也可以使用onBeforeUpdate 生命周期钩子函数验证.

watch侦听器的回调函数会先于onBeforeUpdate钩子函数的回调函数执行.

如果想在侦听器回调中能访问被 Vue 组件更新之后的 DOM,你需要指明 flush: 'post' 选项:

示例:

<template>
  <div>
    <h2>watch</h2>
    <div ref="userRef"> {{ user }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch, reactive } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const user = reactive({ name: '张三', age: 20 })

    // 获取dom节点
    const userRef = ref()

    watch(
      user,
      (nv, ov,) => {
        console.log('侦听器触发了', nv, ov)
        console.log('获取dom节点', userRef.value)
      },
      {
        flush: "post"
      }
    )


    // 修改监听数据源
    const change = () => {
      user.name = '李四'
    }
    return { user, change, userRef }
  },

})
</script>

此时修改数据时,控制台输出:
在这里插入图片描述

在实例中,添加了flush:'post' 选项后, 侦听源修改后, 先更新了vue组件视图, 其次才调用侦听器的回调函数.

如果你此时使用了onUpdated钩子函数, 你会发现具有flush:'post'选项的watch 回调函数,在组件更新后, 但在onUpdated钩子函数前执行.

2.6. watch 返回值: 关闭侦听器

watch 方法返回一个用于关闭侦听器的函数

示例:

<template>
  <div>
    <h2>watch</h2>
    {{ count }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)


    // 侦听响应数据变化
    // watch 返回值stop 是一个用于关闭侦听器的函数
    cosnt stop = watch(
      count,
      (nv, ov) => {
        console.log('新值', nv)  // 100
        console.log('旧值', ov)  // 10
      },
    	// 选项对象
      {
        immediate: true // 初始监听
      }    
    )

    // 3s 以后关闭侦听器
    setTimeout(() => {
      stop()
    }, 3000)

    // 修改监听数据源
    const change = () => {
      count.value++
    }
    return { user, change }
  },

})
</script>

watch侦听器函数返回值是一个用于关闭侦听器的函数, 只要这个函数一执行, 侦听器就会被关闭调, 侦听源数据的变化, 不会在引发watch回调函数的执行.

3. watchEffect

vue2只有一个watch选项用于侦听数据的变化.

但在vue3中, 除了watch侦听数据变化外, 还新增了watchEffect API , watchEffect接受一个回调函数, 会立即执行一次回调函数, 并且收集回调函数中的依赖数据, 以后只要依赖数据发生变化, 都会重新执行回调函数.

3.1. watchEffect 基本使用

watchEffect 接收两个参数:

  1. 监听回调函数,必传参数, 依赖发生变化执行, 默认初始执行
  2. 监听的配置对象

示例:

<template>
  <div>
    <h2>watch</h2>
    <div> fullName:{{ fullName }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watchEffect } from 'vue'

export default defineComponent({
  setup() {
    // state
    const firstName = ref('李')
    const lastName = ref('三')
    const fullName = ref('李 三')

    // watchEffect 回调函数依赖发生变化时重新执行
    watchEffect(() => {
      console.log('watchEffect')
      fullName.value = firstName.value + ' ' + lastName.value
    })


    // 修改监听数据源
    const change = () => {
      firstName.value = '张'
    }
    return {  change, fullName }
  },

})
</script>

3.2. watcEffect 回调函数

第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理副作用的回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求


示例:

<template>
  <div>
    <h2>watch</h2>
    {{ count }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)

  	// 延迟打印参数
    const printInfo = (val: any) => {
      let timer = setTimeout(() => {
        console.log('val', val)
      }, 3000)


      // 清理定时器
      const clearTimer = () => {
        clearTimeout(timer)
      }
      return {
        clearTimer
      }
    }

    
	// 侦听ref数据变化
    watch(
      count,
      (nv, ov, onCleanup) => {
        // 执行printInfo函数, 返回一个清理定时器的函数
        const { clearTimer } = printInfo(nv)

        // 清理副作用函数
        onCleanup(clearTimer)
      },
      {
        immediate: true
      }
    )

  	// 上面的写法等价于下面watchEffect 写法
    watchEffect((onCleanup) => {
      // fullName2.value = firstName.value + ' ' + lastName.value
      const { clearTimer } = printInfo(count.value)
      onCleanup(clearTimer)
    })


   

    // 修改监听数据源
    const change = () => {
      count.value++
    }
    return { user, change }
  },

})
</script>

3.3. watchEffect 第二个参数: 配置对象

第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。

默认情况下,侦听器将在组件渲染之前执行。设置 flush: 'post' 将会使侦听器延迟到组件渲染之后再执行。


示例:

<template>
  <div>
    <h2>watch</h2>
    <div ref="userRef"> {{ user }}</div>
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch, reactive } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const user = reactive({ name: '张三', age: 20 })

    // 获取dom节点
    const userRef = ref()

    watch(
      user,
      (nv, ov,) => {
        console.log('侦听器触发了', nv, ov)
        console.log('获取dom节点', userRef.value)
      },
      {
        flush: "post",
        immediate: true
      }
    )

    // 等价于以下watchEffect用法
    watchEffect(() => {
      console.log('侦听器触发了', user.value)
      console.log('获取dom节点', userRef.value)
    },{
      flush: "post",
    })


    // 修改监听数据源
    const change = () => {
      user.name = '李四'
    }
    return { user, change, userRef }
  },

})
</script>

3.4. watchEffect 返回值:关闭侦听器的函数

watchEffect 返回值与watch 函数一样,都是用于关闭侦听器的函数

示例:

<template>
  <div>
    <h2>watch</h2>
    {{ count }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from 'vue'

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10)


    // 侦听响应数据变化
    // watch 返回值stop 是一个用于关闭侦听器的函数
    cosnt stop = watch(
      count,
      (nv, ov) => {
        console.log('新值', nv)  // 100
        console.log('旧值', ov)  // 10
      },
    	// 选项对象
      {
        immediate: true // 初始监听
      }    
    )

    上面watch 写法等价于以下watchEffect 用法
    const stop = watchEffect(() => {
      console.log('count', count.value)
    })


    // 3s 以后关闭侦听器
    setTimeout(() => {
      stop()
    }, 3000)

    // 修改监听数据源
    const change = () => {
      count.value++
    }
    return { user, change }
  },

})
</script>

4. watch 与 watchEffect 区别

watchwatchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是侦听数据的方式:

  • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调
  • watchEffect则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁

5. 一次性侦听器

vue3在3.4 版本以后增加了一个选项once, 该选项的默认值为false, 当设置为true时, 被侦听源发生变化时,侦听器的回调只会执行一次, 执行完毕后,立即关闭侦听器。

示例:

<template>
  <div>
    <h2>watch</h2>
    {{ count }}
    <button @click="change">修改数据源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, watch } from "vue";

export default defineComponent({
  setup() {
    // 创建一个响应数据
    const count = ref(10);

    // 侦听响应数据变化
    watch(
      count,
      (nv, ov) => {
        console.log("新值", nv); // 100
        console.log("旧值", ov); // 10
      },
      // 选项对象
      {
        once: true, // 只侦听一次, 执行完就关闭侦听器
      }
    );

    // 修改监听数据源
    const change = () => {
      count.value++;
    };
    return { count, change };
  },
});
</script>

6. 结语

至此, 就把计算属性(computed)和常用的两个侦听器(watch, watchEffect)给大家介绍完毕了.

学完本章你需要能够理解一下知识点

  1. computed, watch,watchEffect三个API的基本使用
  2. 理解计算属性computed参数值的不同 与返回值的使用, 返回值是响应式数据
  3. 认识watch侦听器接受三个参数, 侦听源, 回调函数, 选项对象(可选)
  4. 理解watch侦听数据源的不同写法, 回调函数的三个参数的使用, 选项对象中不同选项的功能
  5. 理解watchEffect侦听器的使用, 以及与watch侦听器的不同

如果觉得这篇文章对你有帮助, 点赞关注不迷路, 你的支持是我的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1699950.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++:函数模版简介

如果我们想要实现一个通用的交换函数&#xff0c;在C语言中&#xff0c;我们大概要定义无数个参数不同的交换函数&#xff0c;并且它们的函数名需要各不相同&#xff0c;相信这样的调用便会非常困难&#xff0c;想要调哪个函数还要记得对应的函数名。在C中&#xff0c;有了重载…

Aya 23 是 Cohere For AI 推出的一款最先进的新型多语言开放重量模型

相信一些对LLM关注较高的同学们&#xff0c;应该对这家加拿大的Cohere不会太陌生。毕竟此前&#xff0c;它就开源过 Aya 101 和 Command R 这两款大模型。 Cohere 的非营利性研究实验室 Cohere for AI 发布了 Aya 23&#xff0c;这是其多语言大型语言模型 &#xff08;llm&…

如何使用Rust构建Python原生库?注意,不是动态链接库!!!

参考文档&#xff1a;https://github.com/PyO3/pyo3 创建python虚拟环境&#xff1a; conda create --name pyo3 python3.11.7激活虚拟环境&#xff1a; conda activate pyo3安装依赖&#xff1a; pip install maturin初始化项目&#xff1a; maturin init构建项目&#x…

企业如何做好 SQL 质量管理?

研发人员写 SQL 操作数据库想必一定是一类基础且常见的工作内容。如何避免 “问题” SQL 流转到生产环境&#xff0c;保证数据质量&#xff1f;这值得被研发/DBA/运维所重视。 什么是 SQL 问题&#xff1f; 对于研发人员来说&#xff0c;在日常工作中&#xff0c;大部分都需要…

全国首例!云南破获域名黑产大案,抓获630人

2021年5月以来&#xff0c;在公安部的组织指挥下&#xff0c;云南公安机关历时8个多月&#xff0c;成功破获全国首例域名黑产犯罪案件&#xff0c;经全国各地公安机关连续奋战&#xff0c;共侦破案件300起&#xff0c;抓获涉案人员630人&#xff0c;查封用于黄、赌、诈等违法网…

K8S认证|CKA题库+答案| 16. 升级集群

目录 16、升级集群 CKA v1.29.0模拟系统 下载试用 题目&#xff1a; 开始操作: 1&#xff09;、切换集群 2&#xff09;、 隔离节点 ​3&#xff09;、登录提权 ​4&#xff09;、解锁版本 ​5&#xff09;、查看版本 6&#xff09;、升级版本 7&#xff09;、其他…

字符函数和字符串函数(1)<C语言>

前言 在C语言编写程序时&#xff0c;常常需要完成对字符和字符串的处理&#xff0c;为了快捷、方便处理字符和字符串&#xff0c;C语言内置了一些字符函数和字符串函数&#xff0c;所以下文将要介绍一些字符和字符串函数&#xff0c;如&#xff1a;头文件<ctype.h>包含的…

将list对象里的某一个属性取出组成一个新的list

使用Java8将对象里的某一个属性取出组成一个新的list List<Spgg1> listnew ArrayList<>();Spgg1 spgg1new Spgg1();spgg1.setSpdm("测试");spgg1.setGgdm("001");list.add(spgg1);Spgg1 spgg2new Spgg1();spgg2.setSpdm("测试2");sp…

华为Ascend芯片显卡docker环境搭建并完成YOLO8推理

目前随着AI应用的爆发式增长&#xff0c;支持AI模型训练的显卡硬件也变得炙手可热。目前&#xff0c;由于xx颁布了芯片销售禁令&#xff0c;随着而来的是&#xff0c;在国内高端的英伟达显卡买不到了&#xff0c;中低端的显卡价格也水涨船高。在此环境下&#xff0c;华为技术有…

YAML详情

一、kubernetes支持对象 Kubernetes支持YAML和JSON格式管理资源对象 JSON格式&#xff1a;主要用于api接口之间消息的传递YAML格式&#xff1a;用于配置和管理&#xff0c;YAML是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读 二、YAML语法格式注意点 …

起保停电路工作原理

一、电路组成 起保停电路由电源保护设备&#xff08;空气开关&#xff09;、交流接触器、启动按钮、停止按钮和用电设备组成。 起保停电路的组成部分通常可分为四个部分&#xff1a; 保护部分&#xff1a;&#xff08;空气开关&#xff09;在电流或电压超出一定范围时自动切断…

web学习笔记(五十八)

目录 1. v-model 双向数据绑定 2. 事件修饰符 3. 路径别名 4. setup语法糖 4.1 语法糖的概念 4.2 setup语法糖 5. 配置代理服务器 1. v-model 双向数据绑定 v-model 双向数据绑定只能使用在表单标签&#xff1b; v-model双向数据绑定原理&#xff1a;采用 Object.de…

Unity3D让BoxCollider根据子物体生成自适应大小

系列文章目录 unity工具 文章目录 系列文章目录unity工具 &#x1f449;前言&#x1f449;一、编辑器添加&#x1f449;二、代码动态添加的方法(第一种)&#x1f449;三、代码动态添加的方法(第二种)&#x1f449;四、重新设置模型的中心点&#x1f449;壁纸分享&#x1f449;…

打卡信奥刷题(21)用Scratch图形化工具信奥P7071 [CSP-J2020] 优秀的拆分

使用2进制进行拆分是比较好的解决方案&#xff0c;毕竟对于大家来说二进制转换是非常熟的&#xff0c;如果不会可以参考打卡信奥刷题&#xff08;19&#xff09;用Scratch图形化工具信奥B3972 [语言月赛 202405] 二进制 题解 &#xff0c;输出的时候再转换一下输出&#xff0c;…

【剑指offer】2.2编程语言(p22-p25)——面试题1:string赋值运算函数

本节博客是对阅读剑指offer后的笔记归纳总结&#xff0c;有需要借鉴即可。 目录 1.p21-p25内容概要2.询问语法概念常考&#xff1a;CPP关键字理解举例&#xff1a;sizeof空类 3.分析代码举例&#xff1a;类中拷贝构造的无限递归问题 4.写代码常考点&#xff1a;类内成员函数、迭…

封装了一个iOS水平方向瀑布流布局

首先查看效果图 是支持多分区的 思路就是和竖直方向上的瀑布流布局是一样的&#xff0c; 只不过这里记录的列是水平方向的&#xff0c;同时要记录下 当前最小的x 所在的列&#xff0c;其实原理和竖直方向上的是相同的 &#xff0c;下面贴出代码 父类layout中的代码 // // …

MacOS使用PhpStorm+Xdebug断点调式

基本环境&#xff1a; MacOS m1 PhpStorm 2024.1 PHP7.4.33 Xdebug v3.1.6 1、php.ini 配置 [xdebug] zend_extension "/opt/homebrew/Cellar/php7.4/7.4.33_6/pecl/20190902/xdebug.so" xdebug.idekey "PHPSTORM" xdebug.c…

Java开发大厂面试第26讲:生产环境如何排查问题和优化 JVM?

通过前面几个课时的学习&#xff0c;相信你对 JVM 的理论及实践等相关知识有了一个大体的印象。而本课时将重点讲解 JVM 的排查与优化&#xff0c;这样就会对 JVM 的知识点有一个完整的认识&#xff0c;从而可以更好地应用于实际工作或者面试了。 我们本课时的面试题是&#x…

C++ (week4):Linux系统编程1:文件

文章目录 一、文件&#xff1a;Linux文件操作1.基于文件指针的文件操作2.Linux目录操作(1)目录路径0.error1.getcwd2.chdir3.创建目录&#xff1a;mkdir4.删除目录&#xff1a;rmdir5.unlink(路径名) (2)目录流 DIR*0.模型1.opendir&#xff1a;打开目录流2.closedir&#xff1…

MT3040 矩形覆盖

代码&#xff1a; #include <bits/stdc.h> using namespace std; typedef long long ll; const int N 3e5 10; int n, ans, d, w; stack<int> s; // 单调栈 // 如果楼高度类似121&#xff08;凸&#xff0c;两边相等&#xff0c;中间比两边的大&#xff09;&…