vue3 中的响应式处理---> Ref

7,248 阅读3分钟

基础数据类型转响应式 ---> ref

ref 的用法比较简单,只需要将基本类型的数据传入,通过返回值的 value 属性获取响应式的值即可。

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

其实他也可以接受复杂数据类型作为参数,但是其内部依然是调用 reactive api 进行的响应式处理,这个过程对用户来说是隐藏的。所以,为了使代码更清晰,你应该使用 ref 去处理基本类型的影响式数据,而使用 reactive 去处理复杂类型的数据(注意,这里指的不是所有的负责类型的数据)

ref 的相关 api

ref

ref 是最基本的 api,将你传入的数据包装成响应式数据

isRef

isRef 用于判断传入的参数是不是 ref 包装的响应式数据

const a = ref(1)
const b = 2
cosnole.log(isRef(a)) // true
console.log(isRef(b)) // false

其实内部是判断数据对象上是否包含__v_isRef 属性并且其值为 true

shallowRef

shallowRef 提供了对各种数据类型的包装,他的处理很简单,不论你传入什么值,直接将其挂载在返回值得 value 属性上。使用此 api 时,直接对 value 赋值才会产生响应式变化,否则是不会触发响应式的更新,详情看下面的 triggerRef

const a = shallowRef({a:1})
console.log(isRef(a)) // true
console.log(a.value) // {a:1}
triggerRef

triggerRef 用来主动地触发依赖于某个响应式数据的所有 effect,这个 api 调用场景可能不是很多。

const sref = shallowRef({ a: 1 })
let dummy
effect(() => {
  dummy = sref.value.a
})
console.log(dummy) //1

sref.value.a = 2
console.log(dummy) //1

// 手动调用 triggerRef
triggerRef(sref)
console.log(dummy) //2
unref

unref 返回使用 ref 系列 api 处理的响应式对象的源值,其实就是返回其valiue 属性对应的值。源码如下

function unref(ref){
  return isRef(ref) ? ref.value : ref
}
toRef

toRef 将对象的某个键变为响应式对象,这在 reactive 处理的对象获取属性时某个属性响应式丢失时很有用。

const a = reactive({
  x: 1
})
const x = toRef(a, 'x')
console.log(isRef(x)) // true
console.log(x.value) // 1

// 改变 reactive 数据
a.x = 2
console.log(x.value) // 2

// 改变 ref 数据
x.value = 3
console.log(a.x) //3

let dummyX
effect(() => {
  dummyX = x.value
})
console.log((dummyX === x.value)) // true

// reactive 改变,也会是 toRef 的数据发生改变
a.x = 4
console.log(dummyX)  // 4
toRefs

toRefstoRef 的批量版本。会将传入对象的每个属性处理为 ref 的值。

customRef

customRef api 提供了用户自定义 ref 的操作,下面是他的源码

export function customRef(factory: CustomRefFactory) {
  const { get, set } = factory(
    () => track(r, TrackOpTypes.GET, 'value'),
    () => trigger(r, TriggerOpTypes.SET, 'value')
  )
  const r = {
    __v_isRef: true,
    get value() {
      return get()
    },
    set value(v) {
      set(v)
    }
  }
  return r
}

可以看到,出入的回调函数中需要返回自定义的 getset 函数,就完成了整个自定义 ref 的过程。 vue3 会给你注入 track(在 get 中调用) 和 tirgger (在 set 中调用) 函数,以方便进行数据追踪。

ref 和 shallowRef 的源码实现

function createRef(rawValue, shallow) {
  if (isRef(rawValue)) {
    return rawValue
  }
  let value = shallow ? rawValue : convert(rawValue)
  const r = {
    __v_isRef: true,
    get value() {
      track(r, TrackOpTypes.GET, 'value')
      return value
    },
    set value(newVal) {
      if (hasChanged(toRaw(newVal), rawValue)) {
        rawValue = newVal
        value = shallow ? newVal : convert(newVal)
        trigger(
          r,
          TriggerOpTypes.SET,
          'value',
          __DEV__ ? { newValue: newVal } : void 0
        )
      }
    }
  }
  return r
}

整个代码流程很清晰,其中的 convert 就是 ref api 处理复杂类型的关键,代码如下

const convert = val => isObject(val) ? reactive(val) : val

小结

以上是个人阅读 vue3 ref 源码的记录。