ref
和 reactive
是 Vue3 中实现响应式数据的核心 API。ref
用于包装基本数据类型,而 reactive 用于处理对象和数组。尽管 reactive
似乎更适合处理对象,但 Vue3 官方文档更推荐使用 ref
。
我的想法,ref
就是比reactive
好用,官方也是这么说的,不服来踩!下面我们从源码的角度详细讨论这两个 API,以及 Vue3 为什么推荐使用ref
而不是reactive
?
ref 的内部工作原理
ref
是一个函数,它接受一个内部值并返回一个响应式且可变的引用对象。这个引用对象有一个 .value
属性,该属性指向内部值。
javascript
复制代码
// 深响应式
export function ref(value?: unknown) {
return createRef(value, false)
}
// 浅响应式
export function shallowRef(value?: unknown) {
return createRef(value, true)
}
function createRef(rawValue: unknown, shallow: boolean) {
// 如果传入的值已经是一个 ref,则直接返回它
if (isRef(rawValue)) {
return rawValue
}
// 否则,创建一个新的 RefImpl 实例
return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
// 存储响应式的值。我们追踪和更新的就是_value。(这个是重点)
private _value: T
// 用于存储原始值,即未经任何响应式处理的值。(用于对比的,这块的内容可以不看)
private _rawValue: T
// 用于依赖跟踪的 Dep 类实例
public dep?: Dep = undefined
// 一个标记,表示这是一个 ref 实例
public readonly __v_isRef = true
constructor(
value: T,
public readonly __v_isShallow: boolean,
) {
// 如果是浅响应式,直接使用原始值,否则转换为非响应式原始值
this._rawValue = __v_isShallow ? value : toRaw(value)
// 如果是浅响应式,直接使用原始值,否则转换为响应式值
this._value = __v_isShallow ? value : toReactive(value)
// toRaw 用于将响应式引用转换回原始值
// toReactive 函数用于将传入的值转换为响应式对象。对于基本数据类型,toReactive 直接返回原始值。
// 对于对象和数组,toReactive 内部会调用 reactive 来创建一个响应式代理。
// 因此,对于 ref 来说,基本数据类型的值会被 RefImpl 直接包装,而对象和数组
// 会被 reactive 转换为响应式代理,最后也会被