步骤一:createRef 方法
ref函数定义在packages/reactivity/src/ref.ts文件中:
export function ref(value?: unknown) {
return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
ref 函数实际执行的是 createRef 方法,而该方法实际是返回了一个 RefImpl 构造函数的实例对象:
步骤二:RefImpl 构造函数的实例对象:
class RefImpl<T> {
private _value: T
private _rawValue: T
public dep?: Dep = undefined
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)
}
get value() {
// 依赖收集
trackRefValue(this)
return this._value
}
set value(newVal) {
newVal = this.__v_isShallow ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = this.__v_isShallow ? newVal : toReactive(newVal)
// 依赖触发
triggerRefValue(this, newVal)
}
}
}
步骤三:
RefImpl 构造函数会接收传入的值,可能是基本类型也可能是复杂类型,通过 _rawValue 记录原始值,用于之后依赖触发时新旧值的比较,我们需关注 this._value = __v_isShallow ? value : toReactive(value),toReactive 函数被定义在 packages/reactivity/src/reactive.ts 中:
export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value
步骤四:get方法核心trackRefvalue(this)触发,进行数据的依赖收集
步骤五:set方法中triggerRefValue(this, newVal)进行依赖触发:
总结:
ref函数执行的是createRef方法,该方法实际是返回了一个RefImpl构造函数的实例对象RefImpl构造函数接收传入的值,分为两种情况:提供get value和set value方法- 数据为基本类型,直接返回
- 数据为复杂类型,调用reactive,返回reactive数据
RefImpl提供get value和set value方法get方法核心trackRefvalue(this)触发,进行数据的依赖收集set方法中triggerRefValue(this, newVal)进行依赖触发
为什么 ref 类型数据,必须要通过 .value 访问值呢?
a. 因为 ref 需要处理基本数据类型的响应性,但是对于基本类型数据而言,它无法通过 proxy 建立代理。
b. 而 vue 通过 get value() 和 set value() 定义了两个属性函数,通过主动触发这两个函数(属性调用)的形式来进行依赖收集和依赖触发。
c. 所以我们必须通过 .value 来保证响应性。