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

前言:

上一章, 带大家分析了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侦听器的不同

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

  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 3的组合式API是一种新的编程模式,它使得在Vue组件可以更灵活地组织和复用逻辑。下面是对Vue 3组合式API的介绍: 1. Composition API组合式API):Vue 3引入了Composition API,它允许我们将逻辑按照功能进行组合,而不是按照生命周期钩子进行划分。这样可以更好地组织和复用代码。 2. setup函数:在Vue 3,我们需要在组件使用setup函数来定义组合式API。setup函数在组件创建之前执行,并且接收两个参数:props和context。我们可以在setup函数定义响应式数据、计算属性、方法等。 3. reactive函数:reactive函数是Vue 3用来创建响应式数据的函数。我们可以使用reactive函数将普通对象转换为响应式对象,从而实现数据的双向绑定。 4. ref函数:ref函数是Vue 3用来创建单个响应式数据的函数。与reactive函数不同,ref函数返回一个包装过的对象,我们需要通过.value属性来访问和修改数据。 5. computed函数:computed函数用来创建计算属性。与Vue 2的计算属性类似,我们可以使用computed函数来定义一个依赖其他响应式数据的属性。 6. watch函数:watch函数用来监听响应式数据的变化。我们可以使用watch函数来执行一些副作用操作,比如发送网络请求或者更新DOM。 7. 生命周期钩子:在Vue 3,生命周期钩子函数被废弃了,取而代之的是使用setup函数来处理组件的生命周期逻辑。我们可以在setup函数使用onMounted、onUpdated等函数来模拟Vue 2的生命周期钩子。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值