学习Vue3源码--Ref篇

Posted by Wings on 2020-09-27

Ref

packages\reactivity\src\ref.ts

1
const r = ref(rawValue, [shallow = false])

ref()

1. 执行 createRef(rawValue)
2. 判断 rawValue 是否是一个Ref对象,如果是直接返回,如果不是则实例化 RefImpl 
    2.1 RefImpl 接受 rawValue, shallow 两个参数,设置
        this._rawValue = rawValue;
        this.__v_isRef = true;
        this._shallow = _shallow;
        this._value = convert(rawValue) // 转换; 
4. 执行 convert(_rawValue)
    4.1 convert 判断 _rawValue 是否是 Object 如果不是返回原值,是则 将该对象 用reactive创建
5. 获取 value 时 (get)
    情景一: 未绑定副作用,但获取value
        5.1 执行toRaw(this:当前对象)  toRaw -- 返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发更改。
        5.2 执行 track【轨迹】(目标对象,类型,key) 返回类型值
    情景二:有副作用
        5.1 全局有一个 targetMap 对象 。以 target 为 key 存储一个 depsMap 的 Map对象
        5.2 depsMap 。以 target 上的 key 为 key 创建一个 dep 的 Map 对象
        5.3 dep 存储 副作用  dep.add(activeEffect)
        5.4 全局 activeEffect = reactiveEffect 副作用 activeEffect.deps.push(dep)
        5.5 这里存储的 targetMap 将在 trigger 获取使用
    情景三:
        只创建了值,但是没有执行get/set。不会触发 track。
        depsMap 因为没有执行 track 则不会保存轨迹,所以set直接被return;
6. 设置 value 时 接受一个 newVal (set)
    6.1 先判断 若之前声明 ref 时通过 ref(obj)形式 则不再进入 ref 的set,如果之前是 ref(基本类型)则进入判断。
    6.2 判断新老值是否有变更。若有进入判断
    6.3 this._rawValue = newVal
    6.4 this._value = convert(newVal)
    6.5 执行 trigger 触发更新

shallowRef()

1. 执行createRef(rawValue, true), 返回一个不被 响应式代理 的双向绑定(但是也加入到了副作用数组中)。但是强制更新或者其他响应式更新时,会更新它
原因是 this._value = _shallow = true ? _rawValue : convert(_rawValue);
所以在用 ref.value.xxx = xxx 时,不会进入 ref 的set逻辑。
因为劫持的是 value。
2. 如果 rawValue 是一个 基础类型则不受约束
3. 如果直接修改 ref.value = {} 则不受此约束

isRef

检查一个值是否为一个 ref 对象。 ref 特有标识符 __v_isRef === true

unref(ref)

如果参数是一个 ref 则返回它的 value,否则返回参数本身。它是 val = isRef(val) ? val.value : val 的语法糖。

proxyRefs(objectWithRefs)

判断 objectWithRefs 是否是一个 Reactive,如果是直接返回。
若不是,返回一个 objectWithRefs 的代理:
get: 返回一个 unref(target.key)
set: 若 oldValue 是一个 ref 且 newValue 不是一个 ref 则 oldValue.value = newValue
若不满足上述条件 target.key = newValue

customRef(factory = (track, trigger) => {get:Function,set:Function})

用于自定义一个 ref,可以显式地控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别是用于追踪的 track 与用于触发响应的 trigger,并返回一个带有 get 和 set 属性的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
},
}
})

toRef

可以用来为一个 reactive 对象的属性创建一个 ref。这个 ref 可以被传递并且能够保持响应性。

1. 如果是 Ref 直接返回该 ref
2. 否则进入 ObjectRefImpl 
3. 创建 __v_isRef = true,接受 object 和 key
4. 获取直接返回 this._object[this._key], 赋值直接 this._object[this._key] = newVal
5. 因为 object 是一个 reactive 对象,所以此功能只是给返回对象 打了一个 __v_isRef 标签。
其余逻辑走的还是 reactive 对象的逻辑

toRefs

把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref ,和响应式对象 property 一一对应。

实际是便利传入对象的可枚举属性,然后用 toRef 进行处理