vue3源码解读---ref

154 阅读2分钟

ref接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value 基本数据的响应式都是通过ref来创建。

一:refhook源码目录位置

packages -> reactivity -> src -> ref.ts

ref

  1. ref函数
    export function ref(value?: unknown) { 
        return createRef(value) 
    } 
  1. createRef
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T> 
export function isRef(r: any): r is Ref { 
    return Boolean(r && r.__v_isRef === true) 
}


function createRef(rawValue: unknown, shallow = false) { 
    if (isRef(rawValue)) { 
        return rawValue 
    } 
    return new RefImpl(rawValue, shallow) 
}

isRef判断传入的是不是一个ref对象,如果是就直接return出去。 ref对象会有个__v_isRef属性

RefImpl

  1. RefImpl类的实现
class RefImpl<T> {
    private _value: T
    public readonly __v_isRef = true
    constructor(private _rawValue: T, private readonly _shallow = false) {
        this._value = _shallow ? _rawValue : convert(_rawValue)
    }
    get value() {
        track(toRaw(this), TrackOpTypes.GET, 'value')
        return this._value
    }
    set value(newVal) {
        if (hasChanged(toRaw(newVal), this._rawValue)) {
            this._rawValue = newVal
            this._value = this._shallow ? newVal : convert(newVal)
            trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
        }
    }

}
  • _rawValue:源数据
  • _shallow: 一个布尔值,指示是否应该只进行浅层的响应式转换

constructor中执行

  1. convert函数

export const isObject = (val: unknown): val is Record<any, any> =>{
    return val !== null && typeof val === 'object'
} 
const convert = <T extends unknown>(val: T): T =>{
    return isObject(val) ? reactive(val) : val
} 

isObject函数在源码中的位置: packages -> reactivity -> src -> index.ts 如果传入的数据是对象的话,就走reactive创建响应式。基本数据类型就在RefImpl中做响应式处理

get value 解读

  1. toRaw 函数的目的是获取一个响应式对象背后的原始对象。如果传入的对象不是响应式的,那么直接返回该对象
export const enum ReactiveFlags { 
    SKIP = '__v_skip',  // 标记一个对象或属性应该被Vue的响应式系统跳过。
    IS_REACTIVE = '__v_isReactive', // 标记一个对象是否是响应式的。
    IS_READONLY = '__v_isReadonly', // 标记一个对象是否是只读的
    RAW = '__v_raw' // 用于存储响应式对象的原始版本。
}
export interface Target { 
    [ReactiveFlags.SKIP]?: boolean 
    [ReactiveFlags.IS_REACTIVE]?: boolean 
    [ReactiveFlags.IS_READONLY]?: boolean 
    [ReactiveFlags.RAW]?: any 
}

export function toRaw<T>(observed: T): T {
return (
 (
     observed && toRaw((observed as Target)[ReactiveFlags.RAW])
 ) 
 || observed)
}

track 函数用于追踪依赖关系的函数,以便在数据变化时能够自动通知并更新依赖于这些数据的副作用(effect)或观察者(watcher)

export function track(target: object, type: TrackOpTypes, key: unknown) { 
    if (!shouldTrack || activeEffect === undefined) { 
        return 
    }

    let depsMap = targetMap.get(target) 
    if (!depsMap) { 
        targetMap.set(target, (depsMap = new Map())) 
    }

    let dep = depsMap.get(key) 
    if (!dep) { 
        depsMap.set(key, (dep = new Set())) 
    }

    if (!dep.has(activeEffect)) { 
        dep.add(activeEffect) 
        activeEffect.deps.push(dep) 
        if (__DEV__ && activeEffect.options.onTrack) { 
            activeEffect.options.onTrack({ 
            effect: activeEffect, 
            target, 
            type, 
            key 
            }) 
        } 
    } 
}
  • shouldTrack 全局变量,用于是否以来追踪
  • activeEffect 当前激活的effect
  • targetMap 存储副作用函数的集合

这里就不对track函数做更多的解读,后面会在effect中做介绍

set value 解读

hasChanged函数, 比较一个值是否发生改变,考虑NAN的情况

// compare whether a value has changed, accounting for NaN. 
export const hasChanged = (value: any, oldValue: any): boolean => 
value !== oldValue && (value === value || oldValue === oldValue)

如值发生改变的话,就去修改值,并且通知相关effect,触发响应