欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一步步從Vue3.x源碼上理解ref和reactive的區(qū)別

 更新時間:2023年02月06日 11:06:23   作者:阿遠Carry  
vue3的數據雙向綁定,大家都明白是proxy數據代理,但是在定義響應式數據的時候,有ref和reactive兩種方式,如果判斷該使用什么方式,是大家一直不很清楚地問題,下面這篇文章主要給大家介紹了關于從Vue3.x源碼上理解ref和reactive的區(qū)別的相關資料,需要的朋友可以參考下

前言

對于 refreactive, 應該說是只要用了Vue3.x就會接觸到

因為想要觸發(fā)響應式,就必須通過 refreactive 來實現

但,對于他們理解,大家也是眾說紛紜

那本文將從源碼層面,帶你去理解一下 refreactive 的區(qū)別

?? 此文章基于 Vue 3.2.47 進行分析

使用

ref 可以使用 基本對象 和 引用類型 對象,如:

ref({ num: 1 })

ref(1)

而,reactive 只能使用 引用類型

reactive({ num: 1 })

原理

ref

從源碼上看,ref 方法,就是返回 createRef 的函數調用

// 位置在 /core-3.2.47/packages/reactivity/src/ref.ts 第 82 行
export function ref(value?: unknown) {
  return createRef(value, false)
}

createRef方法就是創(chuàng)建 RefImpl 對象的實例

// 位置在 /core-3.2.47/packages/reactivity/src/ref.ts 第 99 行
function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}

那么,很好理解了,為什么我們打印 ref(1) 會是這樣

那,RefImpl 對象的功能是什么呢?

首先來看 constructor 構造函數

// 位置在 /core-3.2.47/packages/reactivity/src/ref.ts 第 106 行
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)
  }
  // 省略部分代碼...
}

在創(chuàng)建 RefImpl 的實例過程中, 由于在 ref 函數中調用 createRef 傳入第二參數為 false,可以直接理解為

this._rawValue = toRaw(value)

this._value = toReactive(value)

toRaw 在遞歸中檢查對象上是否有 __v_raw,可以理解為是返回原始數據

// 位置在 /core-3.2.47/packages/reactivity/src/reactive.ts 第 239 行
export function toRaw<T>(observed: T): T {
  const raw = observed && (observed as Target)[ReactiveFlags.RAW]
  return raw ? toRaw(raw) : observed
}

// 位置在 /core-3.2.47/packages/reactivity/src/reactive.ts 第 16 行
export const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  IS_SHALLOW = '__v_isShallow',
  RAW = '__v_raw'
}

toReactive 判斷如果他是引用類型的對象,那就使用 reactive 返回對象,如果不是,那就原值返回

// 位置在 /core-3.2.47/packages/reactivity/src/reactive.ts 第 251 行
export const toReactive = <T extends unknown>(value: T): T =>
  isObject(value) ? reactive(value) : value
  
// 位置在 /core-3.2.47/packages/shared/src/index.ts 第 63 行
export const isObject = (val: unknown): val is Record<any, any> =>
  val !== null && typeof val === 'object'

至此我們就是知道了,ref 如果傳入 引用類型的對象底層還是調用 reactive

但是乍一想,好像不對?那 ref 如何進行做響應式的呢?

其實原理在

  // 位置在 /core-3.2.47/packages/reactivity/src/ref.ts 第 118 行
  get value() {
     trackRefValue(this)
     return this._value
   }

  set value(newVal) {
    const useDirectValue =
      this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
    newVal = useDirectValue ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = useDirectValue ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal)
    }
  }

我們都知道,如果想要觸發(fā) ref 的值更新,必須使用 .value,例如:

const num = ref(1)
console.log(num.value)
num.value = 2

我們知道,所謂的響應式其實就是依賴收集派發(fā)更新的過程

對于 console.log(num.value) 我們會觸發(fā) get value 函數,進行依賴收集

對于 num.value = 2 我們會觸發(fā) set value 函數,進行派發(fā)更新

所以

  • ref 對于簡單類型是通過 get value 和 set value 進行依賴收集和派發(fā)更新
  • ref 對于引用類型是通過 reactive 進行依賴收集和派發(fā)更新

但,我們依舊需要注意:

const count = ref({ num: 1 })

count.value.num = 2 // 不會觸發(fā) set value

reactive

從源碼上看,reactive 方法,就是返回 createReactiveObject 的函數調用

// 位置在 /core-3.2.47/packages/reactivity/src/reactive.ts 第 90 行
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (isReadonly(target)) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}

createReactiveObject 方法,使用了 proxy 對值創(chuàng)建的代理對象,并返回代理對象

// 位置在 /core-3.2.47/packages/reactivity/src/reactive.ts 第 181 行
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // target is already a Proxy, return it.
  // exception: calling readonly() on a reactive object
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // only specific value types can be observed.
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)
  return proxy
}

// 位置在 /core-3.2.47/packages/reactivity/src/reactive.ts 第 58 行
function getTargetType(value: Target) {
  return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
    ? TargetType.INVALID
    : targetTypeMap(toRawType(value))
}

// 位置在 /core-3.2.47/packages/reactivity/src/reactive.ts 第 43 行
function targetTypeMap(rawType: string) {
  switch (rawType) {
    case 'Object':
    case 'Array':
      return TargetType.COMMON
    case 'Map':
    case 'Set':
    case 'WeakMap':
    case 'WeakSet':
      return TargetType.COLLECTION
    default:
      return TargetType.INVALID
  }
}

// 位置在 /core-3.2.47/packages/reactivity/src/reactive.ts 第 37 行
const enum TargetType {
  INVALID = 0,
  COMMON = 1,
  COLLECTION = 2
}

現在,我們應該聚焦一下 baseHandlers,因為根據運行上下文可以知道,我們當前的 targetType1, 所以傳入baseHandlers對象

baseHandlers 是從createReactiveObject進行傳入,也就是 mutableHandlers

// 位置在 /core-3.2.47/packages/reactivity/src/baseHandlers.ts 第 225 行
export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}

// 位置在 /core-3.2.47/packages/reactivity/src/baseHandlers.ts 第 48 行
const get = /*#__PURE__*/ createGetter()

// 位置在 /core-3.2.47/packages/reactivity/src/baseHandlers.ts 第 158 行
const set = /*#__PURE__*/ createSetter()

get是通過 createGetter 方法創(chuàng)建

set是通過 createGetter 方法創(chuàng)建

對于 createGetter 返回了一個 get 函數

// 位置在 /core-3.2.47/packages/reactivity/src/baseHandlers.ts 第 94 行
function createGetter(isReadonly = false, shallow = false) {
  return function get(target: Target, key: string | symbol, receiver: object) {
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    } else if (key === ReactiveFlags.IS_SHALLOW) {
      return shallow
    } else if (
      key === ReactiveFlags.RAW &&
      receiver ===
        (isReadonly
          ? shallow
            ? shallowReadonlyMap
            : readonlyMap
          : shallow
          ? shallowReactiveMap
          : reactiveMap
        ).get(target)
    ) {
      return target
    }

    const targetIsArray = isArray(target)

    if (!isReadonly) {
      if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
        return Reflect.get(arrayInstrumentations, key, receiver)
      }
      if (key === 'hasOwnProperty') {
        return hasOwnProperty
      }
    }

    const res = Reflect.get(target, key, receiver)

    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res
    }

    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }

    if (shallow) {
      return res
    }

    if (isRef(res)) {
      // ref unwrapping - skip unwrap for Array + integer key.
      return targetIsArray && isIntegerKey(key) ? res : res.value
    }

    if (isObject(res)) {
      // Convert returned value into a proxy as well. we do the isObject check
      // here to avoid invalid value warning. Also need to lazy access readonly
      // and reactive here to avoid circular dependency.
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }
}

對于 createGetter 返回了一個 set 函數

// 位置在 /core-3.2.47/packages/reactivity/src/baseHandlers.ts 第 161 行
function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    let oldValue = (target as any)[key]
    if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
      return false
    }
    if (!shallow) {
      if (!isShallow(value) && !isReadonly(value)) {
        oldValue = toRaw(oldValue)
        value = toRaw(value)
      }
      if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
        oldValue.value = value
        return true
      }
    } else {
      // in shallow mode, objects are set as-is regardless of reactive or not
    }

    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key)
    const result = Reflect.set(target, key, value, receiver)
    // don't trigger if target is something up in the prototype chain of original
    if (target === toRaw(receiver)) {
      if (!hadKey) {
        trigger(target, TriggerOpTypes.ADD, key, value)
      } else if (hasChanged(value, oldValue)) {
        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
      }
    }
    return result
  }
}

其說白了,reactive 依賴 Proxy將值作為被代理對象,創(chuàng)建代理對象,也是通過getset,進行依賴收集派發(fā)更新

此時,我們也能理解了打印reactive({num: 1})為什么是Proxy 對象

總結

  • ref其實就是創(chuàng)建RefImpl的實例對象,對于 簡單類型 直接通過get valueset value進行依賴收集和派發(fā)更新 ,而對于 引用類型 直接調用 reactive方法
  • reactive底層用了 Proxy對象,創(chuàng)建出代理對象,進行依賴收集和派發(fā)更新

到此這篇關于一步步從Vue3.x源碼上理解ref和reactive區(qū)別的文章就介紹到這了,更多相關Vue3.x ref和reactive的區(qū)別內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • vue使用mixins優(yōu)化組件

    vue使用mixins優(yōu)化組件

    這篇文章主要介紹了vue如何使用mixins優(yōu)化組件,幫助大家更好的理解和學習使用vue框架,感興趣的朋友可以了解下
    2021-04-04
  • Nuxt pages下不同的頁面對應layout下的頁面布局操作

    Nuxt pages下不同的頁面對應layout下的頁面布局操作

    這篇文章主要介紹了Nuxt pages下不同的頁面對應layout下的頁面布局操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • Vue+Element使用富文本編輯器的示例代碼

    Vue+Element使用富文本編輯器的示例代碼

    本篇文章主要介紹了Vue+Element使用富文本編輯器的示例代碼,具有一定的參考價值,有興趣的可以了解一下
    2017-08-08
  • vue 實現一個簡單的全局調用彈窗案例

    vue 實現一個簡單的全局調用彈窗案例

    這篇文章主要介紹了vue 實現一個簡單的全局調用彈窗案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • vue如何實現動態(tài)改變地址欄的參數值

    vue如何實現動態(tài)改變地址欄的參數值

    這篇文章主要介紹了vue如何實現動態(tài)改變地址欄的參數值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • vue實現element表格里表頭信息提示功能(推薦)

    vue實現element表格里表頭信息提示功能(推薦)

    小編最近接了這樣一個需求,需要在element表格操作一欄添加提示功能,下面小編給大家?guī)砹嘶趘ue實現element表格里表頭信息提示功能,需要的朋友參考下吧
    2019-11-11
  • vue-create創(chuàng)建VUE3項目詳細圖文教程

    vue-create創(chuàng)建VUE3項目詳細圖文教程

    create-vue是Vue官方新的腳手架工具,底層切換到了vite(下一代前端工具鏈),為開發(fā)提供極速響應,下面這篇文章主要給大家介紹了關于vue-create創(chuàng)建VUE3項目的相關資料,需要的朋友可以參考下
    2024-03-03
  • Vue enter回車導致頁面刷新問題及解決

    Vue enter回車導致頁面刷新問題及解決

    這篇文章主要介紹了Vue enter回車導致頁面刷新問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Vue自定義指令拖拽功能示例

    Vue自定義指令拖拽功能示例

    本文給大家分享vue自定義指令拖拽功能及自定義鍵盤信息,非常不錯,具有參考借鑒價值,需要的的朋友參考下
    2017-02-02
  • vue中回調函數(callback)的用法舉例

    vue中回調函數(callback)的用法舉例

    這篇文章主要給大家介紹了關于vue中回調函數(callback)的用法舉例,所謂的回調函數,就是由調用函數提供執(zhí)行代碼,被調用函數執(zhí)行完畢之后,再自動執(zhí)行的一個函數,需要的朋友可以參考下
    2023-08-08

最新評論