Vue3中的computed 与 watch 的区别
- 当你想要合成一个新数据用来显示时,选
computed。 - 当你想要在数据变化时搞点事情(调接口、存本地、操作 DOM),选
watch。
computed:计算属性
computed 主要用于数据的衍生与转换。它基于现有的响应式数据计算出一个新值,并且会自动缓存计算结果。只有当它依赖的数据源发生变化时,才会重新计算。
// 写法 1:函数形式(最常用,只读)
computed: {
计算属性名() {
// 依赖 this.xxx 数据
return 计算结果;
}
}
// 写法 2:对象形式(可读可写)
computed: {
计算属性名: {
get() {
return 计算结果;
},
set(val) {
// 当你尝试修改这个计算属性时触发
}
}
}
应用场景:购物车总价
<template>
<div>
<h2>购物车</h2>
<ul>
<li v-for="(item, index) in list" :key="index">
{{ item.name }} - ¥{{ item.price }} x
<button @click="item.count > 1 ? item.count-- : ''">-</button>
{{ item.count }}
<button @click="item.count++">+</button>
</li>
</ul>
<!-- 这里直接使用计算属性 -->
<p>总价:¥{{ totalPrice }}</p>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{ name: '苹果', price: 10, count: 2 },
{ name: '香蕉', price: 5, count: 3 }
]
};
},
computed: {
// 只要 list 里任何商品的 price 或 count 变了,totalPrice 自动更新
totalPrice() {
console.log('computed 重新计算了'); // 验证缓存机制
return this.list.reduce((sum, item) => sum + item.price * item.count, 0);
}
}
};
</script>
为什么用 computed 而不用 methods? 如果你在 methods 里定义一个 getTotalPrice() 函数,虽然也能算出结果,但 computed 有缓存。在上面的例子中,如果你在页面多处调用 totalPrice,computed 只会计算一次;而 methods 每调用一次就会执行一次函数,性能更低。
watch:侦听器
watch 主要用于观察某个数据的变化并执行特定的副作用。它关注的是 “数据变了之后要做什么”,而不是为了得到一个返回值。这里的 “副作用” 通常指:异步请求、DOM 操作、数据持久化等。
watch: {
// 写法 1:简单监听
数据名(newVal, oldVal) {
// newVal 是新值,oldVal 是旧值
// 执行逻辑...
},
// 写法 2:对象形式(开启深度监听、立即执行)
数据名: {
handler(newVal, oldVal) {
// 逻辑...
},
deep: true, // 深度监听:如果监听的是对象,内部属性变化也能触发
immediate: true // 立即执行:页面加载完就先执行一次 handler
}
}
搜索框防抖 :当用户输入停止后,延迟 500ms 再发送请求。
<template>
<div>
<h2>搜索电影</h2>
<input type="text" v-model="searchText" placeholder="请输入关键词..." />
<p v-if="loading">搜索中...</p>
<ul v-else>
<li v-for="movie in movies" :key="movie.id">{{ movie.title }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
searchText: '',
movies: [],
loading: false,
timer: null
};
},
watch: {
// 监听 searchText 的变化
searchText(newVal) {
// 1. 清除上一次的定时器
if (this.timer) clearTimeout(this.timer);
// 2. 如果输入为空,清空列表直接返回
if (!newVal) {
this.movies = [];
return;
}
this.loading = true;
// 3. 设置新的定时器(防抖核心)
this.timer = setTimeout(() => {
this.fetchMovies(newVal);
}, 500);
}
},
methods: {
fetchMovies(keyword) {
// 模拟 API 请求
console.log(`发送请求,关键词:${keyword}`);
setTimeout(() => {
this.movies = [
{ id: 1, title: keyword + '之传奇诞生' },
{ id: 2, title: keyword + '之王者归来' }
];
this.loading = false;
}, 1000);
}
}
};
</script>
为什么这里必须用 watch? 因为我们需要在数据变化后执行 “延迟” 和 “发请求” 这些异步操作,computed 内部必须是同步的 return,无法处理这种场景。
| 维度 | computed | watch |
|---|---|---|
| 返回值 | 必须有 return | 不需要 return |
| 缓存 | 有缓存,性能优先 | 无缓存,变了就执行 |
| 异步 | 不支持异步 | 支持异步操作 |
| 场景 | 多对一:一个值由多个值计算而来 (A+B=C) | 一对多:一个值变化影响多个逻辑 (A 变了,触发 B、C、D) |