一、问题:为什么 reactive 不能解决所有响应式?
Vue3 已经有了 reactive,它可以把对象变成 Proxy。
但问题来了:
const count = 0
基本类型无法被 Proxy 代理。
那怎么办?
Vue3 的答案不是:
修改 JavaScript 语义
而是:
引入一个“值容器抽象”
这就是 ref。
二、ref 的本质:一个“值容器”
ref 不是“响应式变量”。
它是:
一个拥有 getter/setter 的值容器。
当你写:
const count = ref(0)
实际上得到的是:
{
get value()
set value()
}
它通过:
- getter → track
- setter → trigger
完成响应式。
这是一种非常克制的设计。
三、RefImpl —— 标准 ref 的实现逻辑
核心特征:
- 内部持有 Dep
- 使用
.value作为访问边界 - 对象自动转 reactive
这里有一个关键设计:
外层 getter/setter + 内层 Proxy
当访问:
ref.value→ 触发 ref 的依赖ref.value.xxx→ 触发 reactive 的依赖
这是一种分层响应式模型。
细节:为什么会用到 Object.is?
内部使用:
Object.is(newValue, oldValue)
而不是 ===。
原因:
- 区分 +0 / -0
- 正确处理 NaN
- 避免无效 trigger
这是边界值安全设计。
四、CustomRefImpl —— 开放调度权
Vue 并没有把调度逻辑写死。
它允许:
customRef((track, trigger) => {})
这意味着:
响应式系统的调度权被开放给开发者。
典型场景:
- 防抖
- 节流
- 缓存
- 异步控制
这是“框架可扩展性”的体现。
五、ObjectRefImpl —— 视图,而非拷贝
toRef(obj, 'key')
它没有创建新状态。
它只是:
给对象属性套了一层 value 访问接口。
所以:
- 它不持有 Dep
- 它依赖原对象
这是一种“视图抽象”。
非常优雅。
六、GetterRefImpl —— 只读计算视图
toRef(() => x)
特点:
- 只读
- 无独立 Dep
- 依赖来自 getter 内部
这本质上是一个轻量 computed。
七、ref 工厂体系:统一入口抽象
toRef 的真正价值不是转换。
而是:
把多种来源统一为 ref 接口。
无论是:
- 已是 ref
- getter
- 对象属性
- 普通值
最终都变成:
{ value }
这是接口统一设计。
八、proxyRefs —— 模板自动解包的关键
setup 返回一个对象。
模板访问时:
count
实际上需要:
count.value
proxyRefs 做的事情是:
- get 时自动 unref
- set 时自动写回 value
这是语法友好层。 同时是模板自动解包的基础机制。
因为 setup 编译后本质是:
返回一个对象
proxyRefs 负责让模板写法更自然。
九、工程启发:ref 设计的三层抽象
从架构角度看,ref 模块体现了:
- 值容器抽象
- 接口统一
- 调度可扩展
它不是“响应式补丁”。
而是:
把 JS 的值语义提升到可追踪层。