崇序猿的取经之路 vue3 (ref源码)

271 阅读2分钟

1666611198148.jpg

Ref 源码分析

ref的本质是基于类的属性访问器实现的,可以将一个基本类型值进行包装

  1. craeteRef 将普通类型变成一个对象类型
  • ref值也可以是对象,但是一般情况下是对象直接使用reactive更合理
  1. RefImpl 类
  • beta版本之前版本ref是个对象,由于对象不方便拓展,所以改成了类
  • 如果传递 shallow = true 则只是代理最外层
  1. get value 属性访问器
  • track收集依赖
  • 代理 value会帮我们代理到 _value 上
  1. set value 属性设置器
  • 判断新值老值是否改变,改变就更新值
  • trigger 触发依赖执行
import { hasChanged, isObject } from "@vue/shared";
import { track, trigger } from "./effect";
import { TrackOpTypes, TriggerOpTypes } from "./operations";
import { reactive } from "./reactive";

export function ref(value) { // value 是一个基本数据类型
    // 将 普通类型 变成一个 对象类型 
    return createRef(value);
}

export function shallowRef(value) { // shallowRef Api
    return createRef(value, true);
}
function createRef(rawValue, shallow = false) {
    return new RefImpl(rawValue, shallow)
}

const convert = (val) => isObject(val) ? reactive(val) : val; // 递归响应式
class RefImpl {
    private _value;
    public readonly __v_isRef = true; // 产生的实例会被添加 __v_isRef 表示是一个 ref 属性
    constructor(private _rawValue, public readonly _shallow) {
        // 如果是深度,需要把里面的都变成响应式的
        this._value = _shallow ? _rawValue : convert(_rawValue)
    }
    类的属性访问器
    get value() {    // 代理 去value 会帮我们代理到 _value 上
        track(this, TrackOpTypes.GET, 'value');
        return this._value;
    }
    set value(newVal) {
        if (hasChanged(newVal, this._rawValue)) { // 判断新老值是否有变化
            this._rawValue = newVal; // 保存值
            this._value = this._shallow ? newVal : convert(newVal);
            trigger(this, TriggerOpTypes.SET, 'value', newVal);
        }
    }
}

以上为删减版~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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)
    }
 }

可以看见RefImpl class传递了一个泛型类型T,里头具体包含:

  1. 有个私有属性_value,类型为T,有个公开只读属性__v_isRef值为true

  2. 有两个方法,get value(){}和set value(){},分别对应私有属性的读写操作,用于供外界操作value

  3. 有个构造函数constructor,用于构造对象。构造函数接受两个参数:

    • 第一个参数_rawValue,要求是T类型
    • 第二个参数_shallow,默认值为true

    当通过它构建对象时,会给对象的_value属性赋值为 _rawValue或者convert(_rawValue)

    再看convert源码如下:

const convert = <T extends unknown>(val: T): T =>
  isObject(val) ? reactive(val) : val

通过源码我们发现,最终,Vue会根据传入的数据是不是对象isObject(val),如果是对象本质调用的是reactive,否则返回原始数据

  以上文章来源于某度、某知、某金。