Vue 3 computed 和 watch 使用场景划分
FreeGuideOnline
最新
2026-07-05
javascript const firstName = ref('John') const lastName = ref('Doe')
const fullName = computed(() => ${firstName.value} ${lastName.value})
> 任意一个依赖改变,`fullName` 自动更新。
#### 2. 对列表进行过滤、排序、搜索
```javascript
const todos = ref([...])
const hideCompleted = ref(false)
const filteredTodos = computed(() => {
if (hideCompleted.value) {
return todos.value.filter(todo => !todo.completed)
}
return todos.value
})
不需要手动监听
todos或hideCompleted,filteredTodos自动保持为最新状态。
3. 组合多个状态生成布尔标志或计算值
const items = ref([...])
const hasItems = computed(() => items.value.length > 0)
const totalPrice = computed(() =>
items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)
4. 数据格式的统一转换
const rawDate = ref('2025-03-28')
const formattedDate = computed(() => {
return new Date(rawDate.value).toLocaleDateString('zh-CN')
})
关键原则
- 如果一段数据完全由其他数据计算得来,且不需要手动控制更新时机,就应该使用
computed。 - 不要在
computed中执行异步操作、修改其他状态或发送请求。
watch:监听数据变化并执行副作用
watch 用于在特定数据变化时执行有副作用的操作,比如请求数据、操作 DOM、写入本地存储等。
核心特性
- 显式声明要监听的源:可以监听单个
ref、reactive对象的属性、计算属性或是一个返回值的函数。 - 回调参数提供新旧值:可以拿到变化前后的值,方便比较。
- 支持异步操作:回调可以是
async函数,内部可以执行任何操作。 - 深度监听与立即执行:通过
{ deep: true, immediate: true }控制监听行为。
典型适用场景
1. 依据某个数据变化发送网络请求
const userId = ref(1)
const userInfo = ref(null)
watch(userId, async (newId) => {
userInfo.value = await fetch(`/api/users/${newId}`).then(r => r.json())
})
computed无法处理异步请求,这里必须使用watch。
2. 数据变化时同步到外部(如 localStorge、Cookie)
const cart = ref(loadCartFromStorage())
watch(cart, (newCart) => {
localStorage.setItem('cart', JSON.stringify(newCart))
}, { deep: true })
3. 监听多个数据源并执行统一副作用
const keyword = ref('')
const page = ref(1)
watch([keyword, page], ([newKeyword, newPage]) => {
fetchSearchResults(newKeyword, newPage)
})
4. 组件 props 变化时需要执行操作
const props = defineProps({
modelValue: Boolean
})
watch(() => props.modelValue, (newVal) => {
if (!newVal) {
resetForm()
}
})
5. 需要访问 DOM 更新之后的回调(结合 flush: 'post')
const listRef = ref(null)
const items = ref([])
watch(items, () => {
console.log('列表长度', listRef.value?.children.length)
}, { flush: 'post' })
何时避免使用 watch
- 当某个值只是单纯地由其他数据计算而来,没有任何副作用时,用
computed更清晰。 - 多个
watch分别监听不同依赖去更新同一个衍生状态,往往是可以用计算属性替代的坏味道。
场景划分决策指南
| 维度 | computed | watch |
|---|---|---|
| 目的 | 派生出新的状态 | 响应数据变化执行副作用 |
| 关注点 | 结果是什么 | 当什么发生时我要做什么 |
| 返回值 | 必须返回一个值,并作为响应式引用 | 无返回值(或无需关心返回值) |
| 异步支持 | 不支持,函数必须是纯同步的 | 完全支持,回调可以是 async |
| 依赖追踪 | 自动追踪依赖 | 显式指定要监听的源 |
| 缓存 | 有缓存,依赖不变不重新计算 | 无缓存,每次触发都会执行回调 |
| 典型代码 | const x = computed(() => a + b) |
watch(a, (newVal) => { ... }) |
快速决策流程
- 你需要得到一个新值供模板或逻辑使用吗?
→ 是,且该值完全由其他响应式数据算出来 → 用computed - 数据变化时你需要执行动作(请求、存储、手动修改其它状态等)?
→ 是 → 用watch - 你既需要新值,又要执行副作用?
→ 将计算部分抽离为computed,副作用部分单独用watch该计算属性或源数据。
常见误区与最佳实践
误区1:用 watch 来组装衍生数据
// ❌ 不好的做法
const fullName = ref('')
watch([firstName, lastName], ([first, last]) => {
fullName.value = `${first} ${last}`
})
直接用 computed 替换:
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
误区2:在 computed 中执行副作用
// ❌ 错误:计算属性中发送请求
const user = computed(async () => {
return await fetchUser(id.value)
})
应该用 watch 或 watchEffect。
误区3:过度使用 deep: true
深度监听会递归遍历所有嵌套属性,开销较大。
如果只需要监听某个深层属性的变化,可以精确监听:
watch(() => obj.value.nested.key, callback)
而不是:
watch(obj, callback, { deep: true })