Vue 3 Ref源碼解析 (pre-alpha)

上一篇文章說了一下reactive的相關信息,這一節來看看Vue 3中ref的使用方法及相關信息react

附上一篇文章連接:juejin.im/post/5d99de…bash

爲了理解ref的邏輯順序,就直接從ref核心函數一步一步擴展講解吧~函數

export function ref<T>(raw: T): Ref<T> {  
    raw = convert(raw)  
    const v = {    
      _isRef: true,    
      get value() {
      track(v, OperationTypes.GET, '')
      return raw
    },
    set value(newVal) {
      raw = convert(newVal)
      trigger(v, OperationTypes.SET, '')
    }
  }
  return v as Ref<T>
}複製代碼

首先,聲明瞭一個ref,該函數類型爲Ref類型,Ref<T>是一個接口,源碼以下:post

export interface Ref<T> {
  _isRef: true
  value: UnwrapNestedRefs<T>
}複製代碼

ref的核心就是判斷_isRef是否爲true,value值則是存放數據的地方ui

UnwrapNestedRefs是在判斷T(類型)若是是繼承了Ref則使用當前類型,若是不是,使用UnwrapRef類型,源碼以下:spa

export type UnwrapNestedRefs<T> = T extends Ref<any> ? T : UnwrapRef<T>複製代碼

UnwrapRef做用就是當遇到嵌套Ref的時候能正確(使用遞歸)推出業務數據的類型
code

注:Vue 源碼中手動寫了遞歸解析案例對象

⭐因手動寫的案例代碼多,僅截取前兩段代碼(未截取的代碼都是相似)繼承

export type UnwrapRef<T> = T extends Ref<infer V>
  ? UnwrapRef2<V>
  : T extends Array<infer V>
    ? Array<UnwrapRef2<V>>
    : T extends BailTypes
      ? T
       : T extends object ? { [K in keyof T]: UnwrapRef2<T[K]> } : T

type UnwrapRef2<T> = T extends Ref<infer V> 
 ? UnwrapRef3<V>
  : T extends Array<infer V>
    ? Array<UnwrapRef3<V>>
    : T extends BailTypes
      ? T
      : T extends object ? { [K in keyof T]: UnwrapRef3<T[K]> } : T複製代碼

:可以遞歸解析的類型只有ref類型,Array和Object,若是是bailTypes中的類型,則中止遞歸

bailTypes源碼:

type BailTypes =
  | Function
  | Map<any, any>
  | Set<any>
  | WeakMap<any, any>
  | WeakSet<any>複製代碼

回到ref的源碼,介紹了Ref接口以後,接下來就是對傳入的參數進行處理

傳入的參數會被傳入convert中進行處理,源碼以下:

const convert = (val: any): any => (isObject(val) ? reactive(val) : val)複製代碼

即,若是是對象(複雜類型、多層)會將傳入的值交由reactive作響應式處理,不是對象,則返回傳入的值

接下來建立一個v對像,其中傳入_isRef:true(以前說過判斷是否未ref類型的關鍵就是isRef)

判斷isRef的源碼以下:

export function isRef(v: any): v is Ref<any> {
  return v ? v._isRef === true : false
}複製代碼

接着就是v中的get和set方法

const v = {
   _isRef: true,
   get value() {
     track(v, OperationTypes.GET, '')
     return raw
   },
   set value(newVal) {
     raw = convert(newVal)
     trigger(v, OperationTypes.SET, '')
   }
}複製代碼

get方法中有一個track方法,track的做用就是監聽並收集依賴

set方法中就是用convert將newVal值轉換爲響應式賦值給raw,trigger的做用就是執行函數

注:上面的Convert中對於將複雜類型(Object)利用reactive轉爲響應式,而對於簡單類型數據則沒有進行相關處理,是由於當咱們在將參數在函數中傳遞或者解構參數時,會丟失原始數據的引用,所以(如上圖v對象所示),直接用set和get方法對值進行攔截就能夠,而不用再採起Proxy來進行攔截處理

另外,Vue 還提供了一個toRefs的方法,將傳入的對象的key分別存入v對象,將_isRef設置爲true,接着將處理後的key賦值給一個新對象ret

源碼以下:

export function toRefs<T extends object>(
  object: T
): {[K in keyof T]: Ref<T[K]> } {
  const ret: any = {}
  for (const key in object) {
    ret[key] = toProxyRef(object, key)
 }
 return ret
}

function toProxyRef<T extends object, K extends keyof T>(
  object: T,  key: K
): Ref<T[K]> {
  const v = {
    _isRef: true,
    get value() {
      return object[key]
    },
    set value(newVal) {
      object[key] = newVal
    }
  }
  return v as Ref<T[K]>
}複製代碼

ref的相關介紹就到這裏拉~,這是我本身對Vue 3中ref的一些理解,若是哪裏有錯誤的,感謝你們指出拉~~

相關文章
相關標籤/搜索