目录
基础用法
监听单个响应式引用
import { ref, watch } from 'vue'
const count = ref(0)
// 监听单个响应式引用
watch(count, (newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`)
})
// 修改值触发监听
count.value = 1 // 输出: count changed from 0 to 1
监听计算属性
import { ref, computed, watch } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
watch(fullName, (newName, oldName) => {
console.log(`Full name changed from ${oldName} to ${newName}`)
})
监听多个数据源
import { ref, watch } from 'vue'
const firstName = ref('')
const lastName = ref('')
const age = ref(0)
// 监听多个响应式引用
watch([firstName, lastName, age], ([newFirst, newLast, newAge], [oldFirst, oldLast, oldAge]) => {
console.log('Multiple values changed:', {
firstName: { old: oldFirst, new: newFirst },
lastName: { old: oldLast, new: newLast },
age: { old: oldAge, new: newAge }
})
})
监听对象属性
监听对象的特定属性
import { ref, watch } from 'vue'
const user = ref({
name: 'John',
age: 25,
address: { city: 'Beijing', country: 'China' }
})
// 监听对象的特定属性
watch(() => user.value.name, (newName, oldName) => {
console.log(`Name changed from ${oldName} to ${newName}`)
})
// 监听嵌套对象属性
watch(() => user.value.address.city, (newCity, oldCity) => {
console.log(`City changed from ${oldCity} to ${newCity}`)
})
深度监听整个对象
// 深度监听整个对象
watch(user, (newUser, oldUser) => {
console.log('User object changed:', newUser)
}, { deep: true })
// 或者使用 getter 函数
watch(() => user.value, (newUser, oldUser) => {
console.log('User object changed:', newUser)
}, { deep: true })
监听路由变化
import { watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
// 监听路由变化
watch(
() => router.currentRoute.value,
(newRoute, oldRoute) => {
console.log('Route changed:', {
from: oldRoute?.path,
to: newRoute.path,
query: newRoute.query,
params: newRoute.params
})
},
{ immediate: true } // 立即执行一次
)
// 监听路由查询参数
watch(
() => route.query,
(newQuery, oldQuery) => {
console.log('Query params changed:', newQuery)
},
{ deep: true }
)
配置选项
watch(
source, // 要监听的数据源
callback, // 回调函数
{
immediate: true, // 立即执行一次回调
deep: true, // 深度监听对象/数组
flush: 'post', // 执行时机:'pre' | 'sync' | 'post'
onTrack(e) { // 调试:依赖被追踪时调用
console.log('Dependency tracked:', e)
},
onTrigger(e) { // 调试:依赖变化时调用
console.log('Dependency triggered:', e)
}
}
)
flush 选项详解
// 'pre' - 在组件更新前执行
watch(count, callback, { flush: 'pre' })
// 'sync' - 同步执行
watch(count, callback, { flush: 'sync' })
// 'post' - 在组件更新后执行(默认)
watch(count, callback, { flush: 'post' })
实际应用示例
1. 表单数据监听
const formData = ref({
username: '',
password: '',
email: '',
phone: ''
})
// 监听表单数据变化
watch(formData, (newForm) => {
// 表单验证
validateForm(newForm)
// 自动保存草稿
saveDraft(newForm)
}, { deep: true })
// 监听特定字段
watch(() => formData.value.email, (newEmail) => {
if (newEmail) {
validateEmail(newEmail)
}
})
2. 用户权限监听
const userRole = ref('guest')
const permissions = ref([])
watch(userRole, (newRole) => {
// 根据角色更新权限
updatePermissions(newRole)
// 更新菜单显示
updateMenuVisibility(newRole)
})
// 监听权限变化
watch(permissions, (newPermissions) => {
// 更新按钮状态
updateButtonStates(newPermissions)
}, { deep: true })
3. 搜索功能
const searchKeyword = ref('')
const searchResults = ref([])
// 防抖搜索
const debouncedSearch = debounce(async (keyword) => {
if (keyword.length > 2) {
const results = await searchAPI(keyword)
searchResults.value = results
}
}, 300)
watch(searchKeyword, (newKeyword) => {
debouncedSearch(newKeyword)
})
4. 响应式布局
const windowWidth = ref(window.innerWidth)
const isMobile = ref(false)
watch(windowWidth, (newWidth) => {
isMobile.value = newWidth < 768
// 调整布局
adjustLayout(newWidth)
})
// 监听窗口大小变化
window.addEventListener('resize', () => {
windowWidth.value = window.innerWidth
})
5. 数据同步
const localData = ref({})
const serverData = ref({})
// 监听本地数据变化,同步到服务器
watch(localData, async (newData) => {
try {
await syncToServer(newData)
} catch (error) {
console.error('Sync failed:', error)
}
}, { deep: true })
// 监听服务器数据变化,更新本地数据
watch(serverData, (newData) => {
localData.value = { ...newData }
}, { deep: true })
watchEffect 对比
import { watchEffect } from 'vue'
// watchEffect 会自动追踪依赖,无需显式指定
watchEffect(() => {
console.log(`Count is: ${count.value}`)
console.log(`Name is: ${user.value.name}`)
// 自动追踪 count 和 user.name 的变化
})
// 对比 watch
watch([count, () => user.value.name], ([newCount, newName]) => {
console.log(`Count is: ${newCount}`)
console.log(`Name is: ${newName}`)
})
watchEffect 的优势
- 自动追踪依赖
- 代码更简洁
- 适合副作用操作
watch 的优势
- 可以获取旧值
- 更精确的控制
- 可以监听多个不相关的数据源
停止监听
// 获取停止函数
const stopWatcher = watch(count, (newValue) => {
console.log(newValue)
})
// 停止监听
stopWatcher()
// 在组件卸载时自动停止
onUnmounted(() => {
stopWatcher()
})
条件监听
const shouldWatch = ref(true)
const stopWatcher = watch(
count,
(newValue) => {
console.log(newValue)
},
{ immediate: true }
)
// 根据条件停止监听
watch(shouldWatch, (should) => {
if (!should) {
stopWatcher()
}
})
最佳实践
1. 性能优化
// ❌ 避免深度监听大型对象
watch(largeObject, callback, { deep: true })
// ✅ 监听特定属性
watch(() => largeObject.value.importantField, callback)
// ✅ 使用 shallowRef 避免深度监听
const shallowData = shallowRef({ count: 0 })
watch(shallowData, callback) // 不会深度监听
2. 内存管理
// 在组件卸载时清理
onUnmounted(() => {
// 停止所有监听器
stopWatchers.forEach(stop => stop())
})
3. 防抖和节流
import { debounce, throttle } from 'lodash-es'
// 防抖处理
const debouncedCallback = debounce((value) => {
// 处理逻辑
}, 300)
watch(inputValue, debouncedCallback)
// 节流处理
const throttledCallback = throttle((value) => {
// 处理逻辑
}, 1000)
watch(scrollPosition, throttledCallback)
4. 条件执行
watch(
data,
(newData) => {
// 只在满足条件时执行
if (newData.length > 0) {
processData(newData)
}
}
)
5. 错误处理
watch(
asyncData,
async (newData) => {
try {
await processAsyncData(newData)
} catch (error) {
console.error('Processing failed:', error)
// 错误处理逻辑
}
}
)
常见问题
1. 为什么 watch 不触发?
// ❌ 监听非响应式数据
const normalObject = { count: 0 }
watch(normalObject, callback) // 不会触发
// ✅ 使用响应式数据
const reactiveObject = ref({ count: 0 })
watch(reactiveObject, callback, { deep: true })
2. 深度监听的性能问题
// ❌ 深度监听大型对象
watch(largeObject, callback, { deep: true })
// ✅ 监听特定属性
watch(() => largeObject.value.specificField, callback)
// ✅ 使用 watchEffect 自动优化
watchEffect(() => {
// 只追踪实际使用的属性
console.log(largeObject.value.specificField)
})
3. 异步操作的处理
// ✅ 正确处理异步操作
watch(
searchKeyword,
async (newKeyword) => {
if (newKeyword) {
try {
const results = await searchAPI(newKeyword)
searchResults.value = results
} catch (error) {
console.error('Search failed:', error)
}
}
}
)
4. 避免无限循环
// ❌ 可能导致无限循环
watch(data, (newData) => {
data.value = processData(newData) // 修改被监听的数据
})
// ✅ 使用不同的变量
const processedData = ref({})
watch(data, (newData) => {
processedData.value = processData(newData)
})
总结
watch 是 Vue 3 中非常重要的响应式 API,它提供了强大的数据监听能力。合理使用 watch 可以:
- 实现数据的响应式更新
- 处理副作用操作
- 实现组件间的数据同步
- 优化应用性能
记住要根据具体场景选择合适的监听方式,并注意性能优化和内存管理。