大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
计算属性与侦听
这一课你将能做到
- 用
computed派生显示值,并说明相对「方法每次重算」的缓存优势 - 用
watch/watchEffect响应式地跑副作用(如同步 localStorage) - 对照 React:
useMemo思想接近 computed;useEffect接近 watch 系
为什么要学
界面上很多东西是算出来的:过滤后的列表、全名、是否可提交。
把推导塞进模板会又长又难测;用 computed。需要「状态变了顺便做点事」时用 watch。
先建立图像
源状态 ref/reactive
├─ computed → 派生值(有缓存,依赖变才重算)
└─ watch/watchEffect → 副作用(请求、存盘、打日志)
| API | 产出 | 典型用途 |
|---|---|---|
| computed | 值 | 过滤列表、拼接文案 |
| watch | 副作用 | 侦听特定源 |
| watchEffect | 副作用 | 自动收集依赖立即跑 |
跟着做
步骤 1:computed 过滤
<script setup>
import { ref, computed } from "vue";
const filter = ref("all");
const items = ref([
{ id: 1, text: "学 SQL", done: true },
{ id: 2, text: "学 Vue", done: false },
]);
const visible = computed(() => {
if (filter.value === "done") return items.value.filter((t) => t.done);
if (filter.value === "active") return items.value.filter((t) => !t.done);
return items.value;
});
</script>
<template>
<button
v-for="f in ['all', 'active', 'done']"
:key="f"
type="button"
@click="filter = f"
>
{{ f }}
</button>
<ul>
<li v-for="t in visible" :key="t.id">{{ t.text }}</li>
</ul>
</template>
切换 filter,列表应变;visible 不要写成普通函数除非你清楚差别。
步骤 2:computed vs method
function visibleFn() {
return items.value.filter(/* … */);
}
模板里 visibleFn() 每次渲染都调用;visible computed 仅依赖变才重算。简单场景差异小,贵重计算用 computed。
步骤 3:watch 存盘
<script setup>
import { ref, watch } from "vue";
const title = ref(localStorage.getItem("demo-title") || "");
watch(title, (v) => {
localStorage.setItem("demo-title", v);
});
</script>
<template>
<input v-model="title" />
</template>
刷新后输入框应恢复。这是副作用,不该塞进 computed。
步骤 4:watch 多个源与旧值
watch(
[filter, items],
([f, list], [prevF]) => {
console.log("filter", prevF, "→", f, "len", list.length);
},
{ deep: true }
);
对象/数组内容变需要 deep(或侦听更精确的 getter)。
步骤 5:watchEffect
import { watchEffect } from "vue";
watchEffect(() => {
console.log("title now", title.value);
});
自动追踪 title;创建时立刻执行一次。适合简单同步;复杂异步更常用显式 watch。
步骤 6:对照 React
- computed ≈ 依赖明确的 memo 派生
- watch ≈ effect 里跟特定 state
别在 computed 里发请求——两边都把「算值」和「干副作用」分开。
讲清楚要点
- computed 应纯:只根据依赖算值,不改别的状态、不请求网络。
- 可写 computed 少用;入门先掌握只读。
- watch 默认不深拷贝旧对象:比对引用;内容侦听要 deep 或精确路径。
- 清理:
watch可返回清理函数;异步竞态要取消。 - 先问:这是派生还是副作用? 选错 API 是结构味变臭的开始。
- 综合练习课会用 computed 做 Todo 过滤——本课先跑通。
你可能会懵的地方
-
computed 里改依赖?
造成环与难懂更新;拆开。 -
watch第一次不跑?
默认懒;要立即跑加{ immediate: true }。 -
和生命周期
onMounted?
挂载一次的初始化放 onMounted;跟数据变的放 watch。 -
性能焦虑?
先正确,再分析;列表极大再考虑浅层或手工优化。 -
computed(() => props.x)?
可以;或直接用 props。按需。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| computed 里 fetch | 难取消、语义错 | watch / 事件里请求 |
| 模板塞超长过滤 | 难测 | computed |
| watch 忘 deep 却期望内容变触发 | 不回调 | deep 或换侦听方式 |
| 用 watch 硬算派生 UI 字符串 | 多余状态 | computed |
| 无清理的延迟请求 | 竞态错显 | 清理/序号守卫 |
| 把 filter 结果再存一份 ref 手同步 | 双源真相 | 只保留源 + computed |
自测三问
- 何时用 computed 而不是 method?
- 为何不应在 computed 里写 localStorage?
watch与watchEffect怎么选?
小结一句话
computed 缓存派生值,watch 系处理副作用;先分清「算出来」还是「顺便做」。