深入解析 Vue 3 中的 watch 和 watchEffect
深入解析 Vue 3 中的 watch
和 watchEffect
在 Vue 3 中,watch
和 watchEffect
是非常重要的工具,分别用于监听响应式数据的变化,并在变化时执行特定逻辑。它们在数据变化处理、实时副作用、调试等场景中尤为有用。
本文将从以下几个方面详细解析它们的用法和差异,并通过语法糖编写示例代码:
watch
的基础用法watchEffect
的基础用法- 二者的差异与适用场景
- 高级用法与性能优化
- 常见问题与注意事项
一、watch
的基础用法
watch
是 Vue 3 提供的监听器,用于监视特定的响应式数据变化,并在数据发生变化时执行回调。
1. 监听单个响应式数据
示例代码:
<script setup>
import { ref, watch } from 'vue'
// 定义响应式数据
const count = ref(0)
// 监听 count 的变化
watch(count, (newVal, oldVal) => {
console.log(`count 从 ${oldVal} 变为 ${newVal}`)
})
</script>
<template>
<div>
<p>计数器:{{ count }}</p>
<button @click="count++">增加</button>
</div>
</template>
运行说明:
- 当
count
发生变化时,watch
会触发回调,并传入两个参数:newVal
:变化后的新值。oldVal
:变化前的旧值。
2. 监听多个数据
可以使用 watch
同时监听多个响应式数据,传入一个数组。
示例代码:
<script setup>
import { ref, watch } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// 同时监听 firstName 和 lastName 的变化
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`姓名从 ${oldFirst} ${oldLast} 变为 ${newFirst} ${newLast}`)
})
</script>
<template>
<div>
<input v-model="firstName" placeholder="姓" />
<input v-model="lastName" placeholder="名" />
</div>
</template>
3. 深度监听(deep
选项)
默认情况下,watch
不会深度监听对象内部的变化。若要深度监听,可以设置 { deep: true }
。
示例代码:
<script setup>
import { ref, watch } from 'vue'
const user = ref({
name: '张三',
age: 25
})
// 深度监听对象内部的变化
watch(
user,
(newVal, oldVal) => {
console.log('用户信息发生变化:', newVal, oldVal)
},
{ deep: true }
)
</script>
<template>
<div>
<input v-model="user.name" placeholder="名字" />
<input v-model.number="user.age" placeholder="年龄" />
</div>
</template>
注意:
deep
会递归监听对象内部的每一层属性,可能带来性能开销。
4. 立即执行(immediate
选项)
watch
默认不会在初始化时立即执行。如果需要立即触发,可以设置 { immediate: true }
。
示例代码:
<script setup>
import { ref, watch } from 'vue'
const message = ref('Hello Vue 3')
// 监听时立即执行一次回调
watch(
message,
(newVal, oldVal) => {
console.log('立即执行:', newVal)
},
{ immediate: true }
)
</script>
二、watchEffect
的基础用法
watchEffect
是 Vue 3 新增的一个更灵活的工具,可以自动追踪其内部使用的响应式数据,并在这些数据变化时重新执行回调。
1. 基础用法
示例代码:
<script setup>
import { ref, watchEffect } from 'vue'
const count = ref(0)
const double = ref(0)
// 自动追踪响应式数据
watchEffect(() => {
double.value = count.value * 2
console.log(`count: ${count.value}, double: ${double.value}`)
})
</script>
<template>
<div>
<p>计数器:{{ count }}</p>
<p>双倍值:{{ double }}</p>
<button @click="count++">增加</button>
</div>
</template>
2. 自动追踪依赖
与 watch
不同,watchEffect
不需要明确指定要监听的目标,而是会根据回调函数中访问的响应式数据自动追踪依赖。
代码分析:
- 在回调中访问了
count.value
,因此count
被自动添加为依赖。 - 每当
count
发生变化时,watchEffect
会重新执行。
3. 停止监听
通过 watchEffect
返回的函数可以手动停止监听。
示例代码:
<script setup>
import { ref, watchEffect } from 'vue'
const count = ref(0)
// watchEffect 会返回一个函数,这个返回的函数是用来停止监听的。
// 当你调用这个返回的函数时,Vue 会清理与当前 watchEffect 回调关联的依赖
// 并停止触发回调
const stop = watchEffect(() => {
console.log(`count: ${count.value}`)
})
// 停止监听
const stopWatching = () => {
stop()
console.log('已停止监听')
}
</script>
<template>
<div>
<p>计数器:{{ count }}</p>
<button @click="count++">增加</button>
<button @click="stopWatching">停止监听</button>
</div>
</template>
三、watch
与 watchEffect
的对比
特性 | watch | watchEffect |
---|---|---|
依赖声明 | 需要显式指定要监听的响应式数据 | 自动追踪内部访问的响应式数据 |
回调参数 | 提供 newVal 和 oldVal | 无法直接访问变化前后的值 |
立即执行 | 默认不立即执行(可通过 immediate 选项控制) | 默认立即执行 |
适用场景 | 适合特定数据变化时执行操作 | 适合简单逻辑的响应式副作用处理 |
四、高级用法与性能优化
1. 组合 watch
和 computed
在复杂场景下,可以将 computed
与 watch
配合使用,分离逻辑和副作用。
示例代码:
<script setup>
import { ref, computed, watch } from 'vue'
const basePrice = ref(100)
const discount = ref(10)
// 使用 computed 计算最终价格
const finalPrice = computed(() => basePrice.value - discount.value)
// 使用 watch 触发副作用
watch(finalPrice, (newPrice) => {
console.log(`最终价格更新为: ${newPrice}`)
})
</script>
<template>
<div>
<p>最终价格:{{ finalPrice }}</p>
<input v-model.number="basePrice" type="number" placeholder="原价" />
<input v-model.number="discount" type="number" placeholder="折扣" />
</div>
</template>
2. 性能优化:避免深度监听和频繁回调
- 避免在
watch
中使用deep
选项监听大型对象。 - 对于复杂计算,优先使用
computed
。
五、常见问题与注意事项
-
watchEffect
不适合复杂逻辑:- 因为它无法提供变化前后的值,不适合需要对比
newVal
和oldVal
的场景。
- 因为它无法提供变化前后的值,不适合需要对比
-
防止无限循环:
- 如果在
watch
或watchEffect
中直接修改被监听的响应式数据,可能导致无限循环。可以通过条件判断避免这种情况。
- 如果在
六、总结
watch
:适合明确监听指定的响应式数据,常用于特定逻辑处理和副作用操作。watchEffect
:适合自动追踪依赖的响应式数据,快速实现响应式副作用。