承接这两篇博客的分析:
【Vue代码分析】Vue响应式数据驱动视图(一):为什么直接赋值在 Vue 中会失效?
https://blog.csdn.net/weixin_52047874/article/details/162634506?sharetype=blogdetail&sharerId=162634506&sharerefer=PC&sharesource=weixin_52047874&spm=1011.2480.3001.8118【Vue代码分析】Vue响应式数据驱动视图(二):为什么一个布尔值也要用 ref?
https://blog.csdn.net/weixin_52047874/article/details/162635089?sharetype=blogdetail&sharerId=162635089&sharerefer=PC&sharesource=weixin_52047874&spm=1011.2480.3001.8118
在前两篇文章中,我们分别讨论了两个经典场景:
- 为什么列表数据要用
computed派生? - 为什么加载状态要用
ref定义?
这时候,敏锐的开发者往往会把这两个问题放在一起思考:
"这俩东西看起来用法完全不同,
computed是自动算的,ref是手动改的。它们背后的核心原因是一样的吗?"
这是一个直击 Vue 设计灵魂的问题。
答案是:宏观上,是的;微观上,不是。
它们就像一枚硬币的两面,虽然为了解决不同的技术痛点而存在,但归根结底,都在服务于同一个终极目标。
一、 宏观视角:殊途同归的"破壁者"
如果我们把镜头拉远,站在架构的高度看,ref 和 computed 确实在做同一件事:打破 JavaScript 静态逻辑与 Vue 动态视图之间的壁垒。
在原生 JavaScript 中,变量只是内存里的一个代号,DOM 只是浏览器里的一个元素树。这两者本来是老死不相往来的。
- 当你写
let isLoading = true时,JS 变了,但 DOM 不会变。 - 当你写
const list = data.children时,源头变了,但list不会变。
Vue 的响应式系统,本质上就是一个**"连接器"**。
ref的作用 :把一个原始值(如 boolean)变成一个可观察对象,强行插入到 Vue 的管理系统中。computed的作用 :把一个计算逻辑变成一个可观察对象,也强行插入到 Vue 的管理系统中。
核心原因的一致性:
只要你的数据需要**"驱动视图"** ,你就不能使用普通的 JS 变量。你必须使用 ref 或 computed 把数据"注册"进 Vue 的响应式世界里。
如果没有它们,Vue 就成了一个瞎子,看不见数据的流动,也就无法更新页面。
二、 微观视角:解决相反的两个"痛点"
虽然目标一致(建立连接),但它们解决的问题却刚好是相反的。
1. ref 解决的是:"我变了,怎么通知别人?"
在 isLoading 的场景中,痛点在于通知的缺失。
javascript
// 场景:异步加载
isLoading.value = true // 我变了,但我怎么告诉 <van-loading> 组件?
普通的变量赋值是"静默"的。ref 利用 ES6 的 Proxy 或者 getter/setter 劫持了 .value 的赋值操作。
- 核心原理 :拦截修改。
- 作用 :当你修改数据时,
ref会自动触发trigger函数,大喊一声:"数据变了!",从而触发视图更新。
一句话:ref 是为了解决【手动变更后的通知问题】。
2. computed 解决的是:"源头变了,我怎么知道?"
在 subCategoryList 的场景中,痛点在于自动更新的缺失。
javascript
// 场景:二级列表
const subCategoryList = computed(() => categoryList[activeIndex].children)
// activeIndex 变了,subCategoryList 怎么知道要重新计算?
普通的变量赋值是"一次性"的。computed 内部建立了一个精密的依赖收集系统。
- 核心原理 :依赖追踪。
- 作用 :它会记录自己依赖于谁(
activeIndex)。一旦依赖项变了,Vue 会反过来通知computed:"你的源头变了,快重新执行函数计算!",计算出新值后,再触发视图更新。
一句话:computed 是为了解决【源头变更后的自动推导问题】。
三、 深层联系:computed 其实就是"高级版"的 ref
如果你去翻看 Vue 的源码,你会发现一个有趣的秘密:computed 返回的对象,本质上也是一个 ref。
更准确地说,computed 是一个**"只读的、带有自动更新功能的 ref"**。
我们可以这样理解它们的关系:
-
普通
ref:- 值是谁?是你手动塞进去的。
- 什么时候变?你调用
.value = xxx时才变。 - *类比:一个存钱罐,你自己往里扔钱,扔完它就响了。*
-
计算
ref(computed):- 值是谁?是函数运行出来的。
- 什么时候变?是依赖的数据变了,Vue 帮你往里扔新值,然后它响了。
- 类比:一个带有传感器的显示屏,传感器检测到温度(依赖)变了,显示屏自动刷新数字。
所以,当你使用 computed 时,Vue 实际上是在背后帮你创建了一个隐藏的 ref,并帮你写好了一个 watchEffect 来自动更新它。
四、 总结:响应式系统的"双保险"
回到最初的问题:"ref 和 computed 核心原因一样吗?"
- 一样,是因为它们都是 Vue 响应式系统的入口。没有它们,数据流就是断的,视图就不会动。
- 不一样,是因为它们守卫着数据流的不同阶段 。
ref守卫着写入端,确保你的修改能被看见。computed守卫着读取/计算端,确保数据的派生逻辑能被自动执行。
在 Vue 开发中,我们其实就是在不断地做两件事:
- 用
ref定义状态(State)。 - 用
computed定义衍生(Derived State)。
这就是数据驱动视图的完整闭环。