watchEffect 监听不同数据源
watchEffect 会自动追踪在其回调函数中使用的所有响应式依赖,无需显式指定数据源。
1. 监听单个 ref
import { ref, watchEffect } from 'vue'
const count = ref(0)
// 自动追踪 count
watchEffect(() => {
console.log('count 值:', count.value)
// 当 count 变化时自动执行
})
// 修改值会触发
count.value++ // 输出: count 值: 1
2. 监听多个 ref
import { ref, watchEffect } from 'vue'
const count = ref(0)
const name = ref('John')
const age = ref(18)
// 自动追踪所有使用的 ref
watchEffect(() => {
console.log(`姓名: ${name.value}, 年龄: ${age.value}, 计数: ${count.value}`)
// 当 name、age 或 count 任何一个变化时都会执行
})
// 任何修改都会触发
count.value++ // 触发
name.value = 'Jane' // 触发
age.value = 20 // 触发
3. 监听单个 reactive
import { reactive, watchEffect } from 'vue'
const state = reactive({
count: 0,
name: 'John'
})
// 方式1:直接使用整个对象(会深度追踪所有属性)
watchEffect(() => {
console.log('state 整体:', state)
// 当 state 的任何属性变化时都会触发
})
// 方式2:只追踪特定属性(性能更好)
watchEffect(() => {
console.log('count 值:', state.count)
// 只有当 state.count 变化时才触发
})
// 修改会触发
state.count++ // 触发方式1和方式2
state.name = 'Jane' // 只触发方式1
4. 监听多个 reactive
import { reactive, watchEffect } from 'vue'
const user = reactive({
name: 'John',
age: 18
})
const settings = reactive({
theme: 'dark',
language: 'zh'
})
// 自动追踪所有使用的 reactive 属性
watchEffect(() => {
console.log(`用户: ${user.name}, ${user.age}岁`)
console.log(`设置: ${settings.theme}主题, ${settings.language}语言`)
// 当 user.name、user.age、settings.theme、settings.language 任一变化时触发
})
// 修改会触发
user.name = 'Jane' // 触发
settings.theme = 'light' // 触发
5. 混合监听 ref 和 reactive
import { ref, reactive, watchEffect } from 'vue'
const count = ref(0)
const user = reactive({
name: 'John',
info: {
age: 18,
city: 'Beijing'
}
})
// 自动追踪所有使用的响应式数据
watchEffect(() => {
console.log(`计数: ${count.value}`)
console.log(`用户: ${user.name}`)
console.log(`年龄: ${user.info.age}`)
console.log(`城市: ${user.info.city}`)
// 依赖:count.value、user.name、user.info.age、user.info.city
})
// 任何依赖变化都会触发
count.value++ // 触发
user.name = 'Jane' // 触发
user.info.age = 20 // 触发
user.info.city = 'Shanghai' // 触发
6. 监听嵌套 reactive 对象
import { reactive, watchEffect } from 'vue'
const state = reactive({
user: {
profile: {
name: 'John',
address: {
city: 'Beijing',
street: 'Main St'
}
}
}
})
// 深度追踪:会自动追踪所有访问的嵌套属性
watchEffect(() => {
console.log('城市:', state.user.profile.address.city)
console.log('街道:', state.user.profile.address.street)
// 只追踪 city 和 street 的变化
})
// 修改嵌套属性会触发
state.user.profile.address.city = 'Shanghai' // 触发
state.user.profile.address.street = 'Nanjing Rd' // 触发
// 修改未追踪的属性不会触发
state.user.profile.name = 'Jane' // 不会触发(未在回调中使用)
7. watchEffect 的清理和停止
import { ref, watchEffect } from 'vue'
const count = ref(0)
// watchEffect 返回停止函数
const stop = watchEffect((onCleanup) => {
console.log('count:', count.value)
// 清理函数:在重新运行前或停止时执行
onCleanup(() => {
console.log('清理副作用')
// 用于取消请求、清除定时器等
})
})
// 停止监听
stop()
8. 异步 watchEffect
import { ref, watchEffect } from 'vue'
const id = ref(1)
const data = ref(null)
watchEffect(async (onCleanup) => {
let cancelled = false
onCleanup(() => {
cancelled = true
})
// 模拟异步请求
const response = await fetch(`/api/data/${id.value}`)
if (!cancelled) {
data.value = await response.json()
}
})
9. 控制执行时机
import { ref, watchEffect } from 'vue'
const count = ref(0)
// flush: 'pre' (默认) - 组件更新前执行
watchEffect(() => {
console.log('pre:', count.value)
}, {
flush: 'pre'
})
// flush: 'post' - 组件更新后执行
watchEffect(() => {
console.log('post:', count.value)
}, {
flush: 'post'
})
// flush: 'sync' - 同步执行
watchEffect(() => {
console.log('sync:', count.value)
}, {
flush: 'sync'
})
10. watchEffect vs watch 对比
import { ref, reactive, watch, watchEffect } from 'vue'
const count = ref(0)
const state = reactive({ name: 'John', age: 18 })
// watch: 显式指定数据源
watch(count, (newVal, oldVal) => {
console.log('watch - count:', newVal, oldVal)
})
watch(
[() => state.name, () => state.age],
([newName, newAge], [oldName, oldAge]) => {
console.log('watch - name/age:', newName, newAge)
}
)
// watchEffect: 自动追踪依赖
watchEffect(() => {
console.log('watchEffect - count:', count.value)
console.log('watchEffect - name/age:', state.name, state.age)
// 自动追踪 count.value、state.name、state.age
})
// 执行时机
// watch: 懒执行,只有数据变化时才执行
// watchEffect: 立即执行一次,然后依赖变化时执行
实际应用示例
import { ref, reactive, watchEffect } from 'vue'
// 用户搜索示例
const searchKeyword = ref('')
const filters = reactive({
category: 'all',
sortBy: 'date',
priceRange: [0, 1000]
})
const results = ref([])
// 自动搜索:任何搜索条件变化时自动执行
watchEffect(async () => {
console.log('搜索条件变化,重新获取数据')
// 构建查询参数
const params = {
keyword: searchKeyword.value,
category: filters.category,
sortBy: filters.sortBy,
minPrice: filters.priceRange[0],
maxPrice: filters.priceRange[1]
}
// 模拟 API 请求
const response = await fetch(`/api/search?${new URLSearchParams(params)}`)
results.value = await response.json()
})
// 任何条件变化都会触发搜索
searchKeyword.value = 'vue' // 触发搜索
filters.category = 'books' // 触发搜索
filters.sortBy = 'rating' // 触发搜索
filters.priceRange = [0, 500] // 触发搜索
总结对比
| 特性 | watch | watchEffect |
|---|---|---|
| 数据源 | 显式指定 | 自动追踪依赖 |
| 执行时机 | 懒执行(首次不执行) | 立即执行 |
| 旧值获取 | ✅ 可以获取 | ❌ 无法获取 |
| 监听多个 | 需要数组 | 自动收集 |
| 嵌套对象 | 需要 deep 选项 | 自动深度追踪(访问到的属性) |
| 性能优化 | 更精确控制 | 自动优化 |
最佳实践
-
使用 watchEffect 当:
- 不需要获取旧值
- 依赖关系简单且自动
- 需要立即执行副作用
-
使用 watch 当:
- 需要获取旧值
- 需要精确控制监听的数据源
- 需要懒执行(首次不执行)
-
性能优化:
// ❌ 避免:访问过多属性导致频繁执行 watchEffect(() => { console.log(state) // 任何属性变化都触发 }) // ✅ 推荐:只访问需要的属性 watchEffect(() => { console.log(state.name, state.age) // 只有这些属性变化才触发 })