在模板里写一行 {{ sum }},背后却藏着「惰性求值 + 精准依赖收集 + 脏检查缓存」的三重魔法。本文带你钻进源码,拆解 computed 如何决定「何时算、何时不、为何不能异步」。
一、使用方法

看似普通,但注意两点:
- 只读:sum 不是函数,而是一个「带缓存的 getter」。
- 懒执行:直到第一次读取 value,计算函数才真正跑一遍。
二、缓存机制:dirty 标志位
源码核心只有两行状态机:

- dirty 为 true → 需要重新计算
- dirty 为 false → 直接返回旧值
首次读取 sum.value 时,dirty 从 true 变为 false,并把结果存入 value。
当依赖的响应式数据变化,调度器把 dirty 重新置为 true,但不会立即计算,而是等待下一次读取。
这就是「缓存」的本质:用 1 bit 的布尔值换一次昂贵的计算。
三、依赖收集:effect 包裹 getter
computed 的计算函数被 effect 包装成副作用:

- lazy: true 阻止首次执行,实现惰性求值。
- scheduler 在依赖变化时只打标记,不立即重算,确保缓存语义。
当模板读取 sum.value,track 把当前渲染副作用注册到 computed 的依赖图;当 state.a 变化,trigger 通知渲染器重新执行,渲染器再去读 sum.value,此时才真正触发计算。
四、为什么拒绝异步?
设想一个异步 computed:

问题立刻暴露:
- 缓存无法兑现
第一次读取返回一个 Promise,第二次读取依赖并未变化,但缓存里存的是 Promise,无法直接返回结果。
- 渲染时数据缺位
模板在渲染阶段需要同步值,异步导致视图出现空档或闪烁。
- 依赖追踪混乱
异步完成时间不确定,期间若依赖再次变化,无法确定哪一次结果是最新。
Vue 官方给出的替代方案是 watch + ref:

watch 不缓存、不阻塞渲染,天然适合异步副作用。
五、可写 computed:缓存 + setter 的双通道

- getter 走同样的缓存逻辑。
- setter 只是普通函数,无缓存要求,因此可以包含异步(但仍不推荐,由于 setter 触发后 getter 需同步返回新值)。
总结
computed 用「dirty 位 + 惰性 effect」实现同步缓存,用「拒绝异步」换取数据一致性。理解了这一点,你就掌握了 Vue 性能调优的第一把钥匙。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...


