vue3.x源碼學習 Ref 學習筆記1


ref

ref最重要的作用,其實是提供了一套Ref類型,什么是ref類型呢?

ref類型

export interface Ref<T = any> { value: T /** * Type differentiator only. * We need this to be in public d.ts but don't want it to show up in IDE * autocomplete, so we use a private Symbol instead. */ // 用此唯一key,來做Ref接口的一個描述符,讓isRef函數做類型判斷 [RefSymbol]: true /** * @internal */ _shallow?: boolean } 

對於基本數據類型,函數傳遞或者對象解構時,會丟失原始數據的引用,換言之,我們沒法讓基本數據類型,或者解構后的變量(如果它的值也是基本數據類型的話),成為響應式的數據。

// 我們是永遠沒辦法讓`a`或`x`這樣的基本數據成為響應式的數據的,Proxy也無法劫持基本數據。 const a = 1; const { x: 1 } = { x: 1 } 

但是有時候,我們確實就是想一個數字、一個字符串是響應式的,或者就是想利用解構的寫法。那怎么辦呢?只能通過創建一個對象,也即是源碼中的Ref數據,然后將原始數據保存在Ref的屬性value當中,再將它的引用返回給使用者。既然是我們自己創造出來的對象,也就沒必要使用Proxy再做代理了,直接劫持這個value的get/set即可,這就是ref函數與Ref類型的由來。

isRef判斷

如何判斷是否是ref類型,下面是源代碼,還是很簡單的。

  // 判斷是否是Ref 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) } 

創建Ref

那么是怎么創建ref呢?下面我們就講講創建ref。

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

RefImpl構造函數

這里也定義了get/set,沒有任何Proxy相關的操作。

class RefImpl<T> { private _value: T public readonly __v_isRef = true constructor(private _rawValue: T, public readonly _shallow = false) { // 如果是對象就value就是reactive(val),否則就是自己 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) } } } 

convert源碼

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

這里引入了reactive,如果是對象就value就是reactive(val),否則就是自己,reactive暫時先不講,放后面講

toRef&toRefs

toRef也是創建get/set進行攔截,toRefs就是對數組或者對象的屬性進行遞歸設置get/set。

class ObjectRefImpl<T extends object, K extends keyof T> {
  public readonly __v_isRef = true

  constructor(private readonly _object: T, private readonly _key: K) {}

  get value() {
    return this._object[this._key]
  }

  set value(newVal) {
    this._object[this._key] = newVal
  }
}

export function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K
): ToRef<T[K]> {
  return isRef(object[key])
    ? object[key]
    : (new ObjectRefImpl(object, key) as any)
}

export function toRefs<T extends object>(object: T): ToRefs<T> {
  if (__DEV__ && !isProxy(object)) {
    console.warn(`toRefs() expects a reactive object but received a plain one.`)
  }
  const ret: any = isArray(object) ? new Array(object.length) : {}
  for (const key in object) {
    ret[key] = toRef(object, key)
  }
  return ret
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM