Vue3 computed与watch原理及最佳实践
解释Vue3中computed和watch/watchEffect的原理区别、依赖追踪方式,以及使用场景选择。computed的懒计算(lazy)特性是如何实现的?watch的deep和immediate选项如何工作?
回答
小字辈
computed原理与特性
const doubled = computed(() => count.value * 2);
懒计算(Lazy):
- 创建时不执行getter,只在首次读取
.value时计算 - 缓存结果:依赖不变直接返回缓存值
- 脏检查(dirty flag):依赖变化时标记为脏,下次访问重新计算
class ComputedRefImpl {
constructor(getter) {
this._dirty = true; // 初始脏
this._getter = getter;
}
get value() {
if (this._dirty) {
this._value = this._getter(); // 执行计算
this._dirty = false;
}
track(this, 'value');
return this._value;
}
// 依赖变化时:this._dirty = true
}
computed使用场景:
- 依赖state派生新数据(过滤/排序/格式化)
- 需要缓存的昂贵计算
- 不能有副作用(不修改state、不发请求)
watch/watchEffect原理
watch:显式指定监听源,精确控制
watch(source, (newVal, oldVal) => {
// 可获取旧值
}, { deep: true, immediate: true });
watchEffect:自动追踪内部所有响应式依赖
watchEffect(() => {
console.log(count.value, name.value); // 自动追踪
});
deep原理:递归遍历响应式对象的所有属性,在深层get时收集watch作为依赖
immediate原理:创建watch时立即调用一次回调(此时oldVal为undefined)
选择指南
| 场景 | 选择 |
|---|---|
| 派生数据,需缓存 | computed |
| 异步操作(请求/定时器) | watch |
| 需要旧值对比 | watch |
| 多个依赖,自动追踪 | watchEffect |
| DOM操作或非响应式副作用 | watchPostEffect |
最佳实践:
- computed不要修改响应式state
- watchEffect用于日志、同步localStorage
- watch使用
{ immediate: true }替代手动调用一次 - 及时通过
watchEffect/watch返回的stop函数清理监听