Vue 3 中 computed
与 watch
深度解析
在 Vue 3 组合中,响应式工具的类型安全使用至关重要。以下是详细说明
一、watch
侦听器
1. 基础类型监听
<template><div>实际参数1={{count}}</div><div><button @click="count++">点击</button></div>
</template><script setup lang="ts">
import {reactive,ref,watch
} from "vue";const count = ref<number>(0)
const state = reactive({items: [] as string[]});watch(count, (newVal:number, oldVal:number) => {state.items.push(String(count.value))console.log('newVal, oldVal === ', newVal, oldVal)
})watch(() => state.items, () => {console.log('state.items ===', state.items)
},{deep: true})</script>
2. 多源监听
<template><div>实际参数1={{count}}</div><div><button @click="count++">点击</button></div>
</template><script setup lang="ts">
import {reactive,ref,watch
} from "vue";const count = ref<number>(0)
const state = reactive({items: [] as string[]});watch(count, (newVal:number, oldVal:number) => {state.items.push(String(count.value))console.log('newVal, oldVal === ', newVal, oldVal)
})watch(() => state.items, () => {console.log('state.items ===', state.items)
},{deep: true})watch([count, state], ([newCount, newState]:[number, object], [oldCount, oldState]:[number, object])=> {console.log('[newCount, newState], [oldCount, oldState] =', newCount, newState, oldCount, oldState)
})
</script>
3. 深度监听对象
<template><div>实际参数1={{product}}</div><div><button @click="product.price++">点击</button></div>
</template><script setup lang="ts">
import {reactive,watch
} from "vue";
interface Product {id: number,price: number,name: string,specs: {color: string,weight: number}
}const product = reactive<Product>({id: 1,price: 131,name: 'Bwm',specs: {color: 'red',weight: 80}
})watch(() => product.price, // 创建新引用触发深度监听(newProduct,oldProduct) => {console.log('newVal,oldVal === ', newProduct,oldProduct)},{deep: true})
</script>
二、watchEffect
高级用法
watchEffect() 允许我们自动跟踪回调的响应式依赖,且会立即执行。
1. 基本用法
<template><div>实际参数1={{product}}</div><div><button @click="product.price++">点击</button></div>
</template><script setup lang="ts">
import {reactive,ref,watch, watchEffect
} from "vue";
interface Product {id: number,price: number,name: string,specs: {color: string,weight: number}
}const product = reactive<Product>({id: 1,price: 131,name: 'Bwm',specs: {color: 'red',weight: 80}
})
const totalPrice = ref<number>(0)watchEffect(()=> {totalPrice.value = product.price + 2console.log('totalPrice === ', totalPrice)
})
</script>
2. 清理副作用
<template><div>实际参数1={{product}}</div><div><button @click="product.price+=10">点击</button></div>
</template><script setup lang="ts">
import {reactive,ref,watchEffect,onWatcherCleanup
} from "vue";interface Product {id: number,price: number,name: string,specs: {color: string,weight: number}
}const product = reactive<Product>({id: 1,price: 131,name: 'Bwm',specs: {color: 'red',weight: 80}
})
const totalPrice = ref<number>(0)watchEffect(()=> {totalPrice.value = product.price + 2console.log('totalPrice === ', totalPrice)onWatcherCleanup(() => {console.log('onWatcherCleanup ===')})
})
</script>
三、computed
计算属性
1. 对象类型计算
<template><div>实际参数1={{totalPrice}}</div><div><button @click="product.price+=10">点击</button></div>
</template><script setup lang="ts">
import {reactive,ref,computed
} from "vue";
interface Product {id: number,price: number,name: string,specs: {color: string,weight: number}
}const product = reactive<Product>({id: 1,price: 131,name: 'Bwm',specs: {color: 'red',weight: 80}
})
const totalPrice = computed<number>(()=>product.price += 10)
</script>
2. 可写计算属性
<template><div>{{fullName}}</div><div>姓名: <el-input v-model="fullName"/></div>
</template><script setup lang="ts">
import {reactive,ref,computed
} from "vue";
const firstName = ref<string>('Jane')
const lastName = ref<string>('Smith')const fullName = computed<string>({get() {return `${firstName.value} ${lastName.value}`},set(fullName:string) {const [newFirstName, newLastName] = fullName.split(' ')firstName.value = newFirstNamelastName.value = newLastName}
})
</script>
四、computed
vs watch
vs watchEffect
对比
特性 | computed | watch | watchEffect |
---|---|---|---|
目的 | 派生值 | 响应变化执行操作 | 自动追踪依赖执行操作 |
返回值 | ref对象 | 停止函数 | 停止函数 |
初始化执行 | 立即计算 | 可配置(immediate) | 立即执行 |
依赖声明 | 自动 | 显式指定 | 自动 |
缓存 | ✅ | ❌ | ❌ |
异步支持 | ❌ | ✅ | ✅ |
清理机制 | ❌ | ✅ | ✅ |
调试钩子 | ❌ | ✅ | ✅ |
适合场景 | 数据转换/格式化 | 精确控制的操作 | 自动追踪依赖的副作用 |
总结
-
computed
:- 用于派生状态
- 具有缓存机制
- 适合数据转换和格式化
- 模板中优先使用
-
watch
:- 用于执行副作用
- 提供精确控制
- 适合异步操作、DOM操作
- 需要显式声明依赖
-
watchEffect
:- 自动追踪依赖
- 立即执行
- 适合多个依赖的简单副作用
- 提供清理机制
黄金法则:
- 需要派生值 → 用
computed
- 需要响应变化执行操作 → 用
watch
或watchEffect
- 需要精确控制依赖 → 用
watch
- 需要自动追踪多个依赖 → 用
watchEffect
- 避免在
computed
中产生副作用 - 总是在副作用中清理资源
通过合理选择和使用这些 API,可以构建出高效、可维护的 Vue 3 应用程序。