前言
在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
来存储临时数据。