Vue3的watchEffect的妙用,与watch的区别

前言

在Vue3中,引入了Composition API,其中的watchEffect()函数是一个非常强大和灵活的工具,用于处理响应式数据的变化,使得项目更加弹性和灵活。它与watch有所不同,本文将介绍watchEffect()的定义、特点、与watch的区别以及使用时的注意事项。

一、定义

watchEffect()函数用于创建一个自动追踪依赖的响应式副作用。它会在初始化时会立即执行一次,并自动追踪在回调函数中使用的所有响应式数据,在这些数据发生变化时重新运行回调函数。

例如,在每当 todoId 的引用发生变化时使用侦听器来加载一个远程资源,如果用watch,是这么写:

<template>
  <div>Test</div>
</template>

<script setup>
  import { ref, watch } from 'vue'
  const todoId = ref(1)
  const data = ref(null)

  watch(
    todoId,
    async () => {
      const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
      data.value = await response.json()
      console.log(data.value)
    },
    { immediate: true }
  )
</script>

打印:

但是用了watchEffect()就可以简化为:

<template>
  <div>Test</div>
</template>

<script setup>
  import { ref, watchEffect } from 'vue'
  const todoId = ref(1)
  const data = ref(null)

  watchEffect(async () => {
    const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
    data.value = await response.json()
    console.log(data.value)
  })
</script>

 打印:

两者都立即打印了data。但下面的例子中,回调会立即执行,不需要指定 immediate: true。在执行期间,它会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。有了 watchEffect(),我们不再需要明确传递 todoId 作为源值。

从这个角度来看,watchEffect()的作用类似于Vue2中的computed,即依赖项发生变化,自己也跟着改变。但与computed不同,watchEffect()没有返回值,而是直接执行回调函数。

二、特点

watchEffect()自动追踪其回调函数中使用的所有响应式数据,并在这些数据发生变化时重新运行回调函数。这使得我们无需手动指定要监听的具体属性,减少了代码的冗余。

    对于这种只有一个依赖项的例子来说,watchEffect() 的好处相对较小。但是对于有多个依赖项的侦听器来说,使用 watchEffect() 可以消除手动维护依赖列表的负担。

    此外,如果你需要侦听一个嵌套数据结构中的几个属性,watchEffect() 可能会比深度侦听器更有效,因为它将只跟踪回调中被使用到的属性,而不是递归地跟踪所有的属性。 

多个依赖项例子:

<template>
  <div>Test</div>
</template>

<script setup>
  import { reactive } from 'vue'
  const state = reactive({
    count: 0,
    name: 'John',
    age: 25
  })

  // 使用watchEffect()监听count和name的变化
  watchEffect(() => {
    console.log('count or name changed:', state.count, state.name)
  })

  // 模拟count和name变化
  setTimeout(() => {
    state.count = 1 // 输出'count or name changed: 1 John'
    state.name = 'Alice' // 输出'count or name changed: 1 Alice'
  }, 1000)
</script>

打印:

深度依赖例子:

<template>
  <div>Test</div>
</template>

<script setup>
  import { reactive, watchEffect } from 'vue'
  const state = reactive({
    person: {
      name: 'John',
      age: 25
    },
    todos: [
      { text: 'Task 1', completed: false },
      { text: 'Task 2', completed: true },
      { text: 'Task 3', completed: false }
    ]
  })

  // 使用watchEffect()监听person对象中name和age的变化
  watchEffect(() => {
    console.log('person changed:', state.person.name, state.person.age)
  })

  // 模拟person中name和age的变化
  setTimeout(() => {
    state.person.name = 'Alice' // 输出'person changed: Alice 25'
    state.person.age = 30 // 输出'person changed: Alice 30'
  }, 1000)
</script>

打印:

三、与watch的区别

1. 监听方式不同watchEffect()是自动追踪所有使用的响应式数据,而watch需要手动指定要监听的特定属性。

2. 监听粒度不同watchEffect()监听的是响应式数据的整个变化,而watch可以侦听指定的属性或表达式的变化。

3. 计算属性处理方式不同:对于计算属性,watchEffect()会自动依赖于计算属性使用到的响应式数据,而watch需要手动指定计算属性为监听目标。

四、注意事项

1. 避免过度监听:由于watchEffect()会追踪使用到的所有响应式数据,因此要确保在回调函数中只使用必要的响应式数据,避免造成不必要的渲染开销。

2. 异步操作需谨慎处理:由于watchEffect()会立即执行回调函数,如果在回调函数中进行异步操作,需要谨慎处理,以免导致意外行为或副作用。

<script setup>
import { watchEffect } from 'vue'

// 它会自动停止
watchEffect(() => {})

// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>

3. 避免无限循环:当在watchEffect()的回调中修改响应式数据时,可能会导致无限循环。要避免此问题,可以使用watch()函数并设置immediate: true选项,或者使用ref来存储临时数据。

猜你喜欢

转载自blog.csdn.net/weixin_42373175/article/details/131780643