Vue3 源码解析系列 - Ref

168 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情

前言

Ref 接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value 属性,指向该内部值。它和同样是能把数据变成响应式的 reactive 有什么区别呢?

  1. reactive 一般推荐用来定义复杂的数据类型,ref一般推荐用于定义基本类型。
  2. ref操作数据需要通过 .value 来访问,在template模板中则不需要。而reactive都不需要通过.value访问

使用

ref 方法接受一个值,变为响应式,然后访问时需要通过这个响应式对象的 value 属性啦哎访问

import { ref } from 'vue'

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

通过ref方法的类型声明为:

interface Ref<T> {
  value: T
}
function ref<T>(value: T): Ref<T>

可以看到,里面只有一个 value 属性。

源码

ref 的方法在 /packages/reactivity/src/ref.ts 文件中。

// packages/reactivity/src/ref.ts
export function ref(value?: unknown) {
  return createRef(value, false)
}

里面只做了一件事,返回 createRef 的值,createRef 接受两个参数,value和shallow,shallow 表明是否为 shallowRef 的浅层响应式 api。

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

如果value 值已经时 ref 对象,则直接返回,否则通过实例化类 RefImpl 来创建ref对象。

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 中有私有变量 _value 和 _rawValue,它们分别代表,ref的最新的值和旧值。
只读变量 __v_isRef 用来标识这个对象为 ref 类型的对象。
公共变量 dep 用来保存依赖。

里面有两个方法 get valueset value,当我们通过 .value 访问值的时候,就会触发get value,设置value的时候就会触发set value

getter 中通过 trackRefValue 进行收集依赖。track 具体实现我们在上一篇已经详细讲过,这里的 trackRefValue 原理是一样的。
setter 中首先判断值是否改变了,如果改变则进行重新赋值,和触发依赖进行派发更新。

小结

ref 的总体代码还是比较简单的,也是通过 gettersetter进行依赖收集和更新。