久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      詳解vue3中reactive和ref的區(qū)別(源碼解析)

      vue中reactive和ref的區(qū)別是什么?下面本篇文章帶大家深入源碼徹底搞清vue3中reactive和ref的區(qū)別,希望對(duì)大家有所幫助!

      詳解vue3中reactive和ref的區(qū)別(源碼解析)

      如何快速入門(mén)VUE3.0:進(jìn)入學(xué)習(xí)

      在vue3的日常開(kāi)發(fā)中,我發(fā)現(xiàn)很多人都是基于自己的習(xí)慣reactiveref一把梭,雖然這樣都可以實(shí)現(xiàn)需求,既然這樣那為什么已經(jīng)有了reactive還需要再去設(shè)計(jì)一個(gè)ref呢?這兩者的實(shí)際運(yùn)用場(chǎng)景以及區(qū)別是什么呢?

      并且關(guān)于ref的底層邏輯,有的人說(shuō)ref的底層邏輯還是reactive。有的人說(shuō)ref的底層是class,value只是這個(gè)class的一個(gè)屬性,那這兩種說(shuō)法哪種正確呢?都有沒(méi)有依據(jù)呢?

      抱著這樣的疑問(wèn)我們本次就深入源碼,徹底搞清vue3中reactiveref的區(qū)別。(學(xué)習(xí)視頻分享:vue視頻教程)

      不想看源碼的童鞋,可以直接拉到后面看總結(jié)

      reactive

      源碼地址:packages/reactivity/reactive.ts

      首先我們看一下vue3中用來(lái)標(biāo)記目標(biāo)對(duì)象target類型的ReactiveFlags

      // 標(biāo)記目標(biāo)對(duì)象 target 類型的 ReactiveFlags export const enum ReactiveFlags {   SKIP = '__v_skip',   IS_REACTIVE = '__v_isReactive',   IS_READONLY = '__v_isReadonly',   RAW = '__v_raw' }  export interface Target {   [ReactiveFlags.SKIP]?: boolean          // 不做響應(yīng)式處理的數(shù)據(jù)   [ReactiveFlags.IS_REACTIVE]?: boolean   // target 是否是響應(yīng)式   [ReactiveFlags.IS_READONLY]?: boolean   // target 是否是只讀   [ReactiveFlags.RAW]?: any               // 表示proxy 對(duì)應(yīng)的源數(shù)據(jù), target 已經(jīng)是 proxy 對(duì)象時(shí)會(huì)有該屬性 }

      reactive

      export function reactive<T extends object>(target: T): UnwrapNestedRefs<T> export function reactive(target: object) {   // if trying to observe a readonly proxy, return the readonly version.   // 如果目標(biāo)對(duì)象是一個(gè)只讀的響應(yīng)數(shù)據(jù),則直接返回目標(biāo)對(duì)象   if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {     return target   }   // 創(chuàng)建 observe   return createReactiveObject(     target,     false,     mutableHandlers,     mutableCollectionHandlers,     reactiveMap   ) }

      reactive函數(shù)接收一個(gè)target對(duì)象,如果target對(duì)象只讀則直接返回該對(duì)象

      若非只讀則直接通過(guò)createReactiveObject創(chuàng)建observe對(duì)象

      createReactiveObject

      看著長(zhǎng)不要怕,先貼createReactiveObject完整代碼,我們分段閱讀

      /**  *   * @param target 目標(biāo)對(duì)象  * @param isReadonly 是否只讀  * @param baseHandlers 基本類型的 handlers  * @param collectionHandlers 主要針對(duì)(set、map、weakSet、weakMap)的 handlers  * @param proxyMap  WeakMap數(shù)據(jù)結(jié)構(gòu)  * @returns   */  function createReactiveObject(   target: Target,   isReadonly: boolean,   baseHandlers: ProxyHandler<any>,   collectionHandlers: ProxyHandler<any>,   proxyMap: WeakMap<Target, any> ) {    // typeof 不是 object 類型的,在開(kāi)發(fā)模式拋出警告,生產(chǎn)環(huán)境直接返回目標(biāo)對(duì)象   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   // 已經(jīng)是響應(yīng)式的就直接返回(取ReactiveFlags.RAW 屬性會(huì)返回true,因?yàn)檫M(jìn)行reactive的過(guò)程中會(huì)用weakMap進(jìn)行保存,   // 通過(guò)target能判斷出是否有ReactiveFlags.RAW屬性)   // 例外:對(duì)reactive對(duì)象進(jìn)行readonly()   if (     target[ReactiveFlags.RAW] &&     !(isReadonly && target[ReactiveFlags.IS_REACTIVE])   ) {     return target   }   // target already has corresponding Proxy   // 對(duì)已經(jīng)Proxy的,則直接從WeakMap數(shù)據(jù)結(jié)構(gòu)中取出這個(gè)Proxy對(duì)象   const existingProxy = proxyMap.get(target)   if (existingProxy) {     return existingProxy   }   // only a whitelist of value types can be observed.   // 只對(duì)targetTypeMap類型白名單中的類型進(jìn)行響應(yīng)式處理   const targetType = getTargetType(target)   if (targetType === TargetType.INVALID) {     return target   }   // proxy 代理 target   // (set、map、weakSet、weakMap) collectionHandlers   // (Object、Array) baseHandlers   const proxy = new Proxy(     target,     targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers   )   proxyMap.set(target, proxy)   return proxy }

      首先我們看到createReactiveObject接收了五個(gè)參數(shù)

        target: Target,   isReadonly: boolean,   baseHandlers: ProxyHandler<any>,   collectionHandlers: ProxyHandler<any>,   proxyMap: WeakMap<Target, any>

      target 目標(biāo)對(duì)象

      isReadonly 是否只讀

      baseHandlers 基本類型的 handlers 處理數(shù)組,對(duì)象

      collectionHandlers 處理 set、map、weakSet、weakMap

      proxyMap WeakMap數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)副作用函數(shù)


      這里主要是通過(guò)ReactiveFlags.RAWReactiveFlags.IS_REACTIVE判斷是否是響應(yīng)式數(shù)據(jù),若是則直接返回該對(duì)象

       if (     target[ReactiveFlags.RAW] &&     !(isReadonly && target[ReactiveFlags.IS_REACTIVE])   ) {     return target   }

      對(duì)于已經(jīng)是Proxy的,則直接從WeakMap數(shù)據(jù)結(jié)構(gòu)中取出這個(gè)Proxy對(duì)象并返回

        const existingProxy = proxyMap.get(target)   if (existingProxy) {     return existingProxy   }

      這里則是校驗(yàn)了一下當(dāng)前target的類型是不是Object、Array、Map、SetWeakMap、WeakSet,如果都不是則直接返回該對(duì)象,不做響應(yīng)式處理

       // 只對(duì)targetTypeMap類型白名單中的類型進(jìn)行響應(yīng)式處理   const targetType = getTargetType(target)   if (targetType === TargetType.INVALID) {     return target   }

      校驗(yàn)類型的邏輯

      function getTargetType(value: Target) {   return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)     ? TargetType.INVALID     : targetTypeMap(toRawType(value)) }  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   } }

      所有的前置校驗(yàn)完后,就可以使用proxy 代理target對(duì)象了

      這里使用了一個(gè)三目運(yùn)算符通過(guò)TargetType.COLLECTION來(lái)執(zhí)行不同的處理邏輯

      • (set、map、weakSet、weakMap) 使用 collectionHandlers
      • (Object、Array) 使用 baseHandlers
      // proxy 代理 target   // (set、map、weakSet、weakMap) collectionHandlers   // (Object、Array) baseHandlers   const proxy = new Proxy(     target,     targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers   )   proxyMap.set(target, proxy)   return proxy

      現(xiàn)在對(duì)createReactiveObject的執(zhí)行邏輯是不是就很清晰了

      到這里還沒(méi)有結(jié)束,createReactiveObject中最后proxy是如何去代理target的呢?這里我們用baseHandlers舉例,深入baseHandlers的內(nèi)部去看看

      baseHandlers

      源碼地址:packages/reactivity/baseHandlers.ts

      reactive.ts中我們可以看到一共引入了四種 handler

      import {   mutableHandlers,   readonlyHandlers,   shallowReactiveHandlers,   shallowReadonlyHandlers } from './baseHandlers'
      • mutableHandlers 可變處理
      • readonlyHandlers 只讀處理
      • shallowReactiveHandlers 淺觀察處理(只觀察目標(biāo)對(duì)象的第一層屬性)
      • shallowReadonlyHandlers 淺觀察 && 只讀

      我們以mutableHandlers為例

      // 可變處理 // const get = /*#__PURE__*/ createGetter() // const set = /*#__PURE__*/ createSetter() // get、has、ownKeys 會(huì)觸發(fā)依賴收集 track() // set、deleteProperty 會(huì)觸發(fā)更新 trigger() export const mutableHandlers: ProxyHandler<object> = {   get,                  // 用于攔截對(duì)象的讀取屬性操作   set,                  // 用于攔截對(duì)象的設(shè)置屬性操作   deleteProperty,       // 用于攔截對(duì)象的刪除屬性操作   has,                  // 檢查一個(gè)對(duì)象是否擁有某個(gè)屬性   ownKeys               // 針對(duì) getOwnPropertyNames,  getOwnPropertySymbols, keys 的代理方法 }

      這里的getset分別對(duì)應(yīng)著createGetter()、createSetter()

      • createGetter()

      先上完整版代碼

      /**  * 用于攔截對(duì)象的讀取屬性操作  * @param isReadonly 是否只讀  * @param shallow 是否淺觀察  * @returns   */ function createGetter(isReadonly = false, shallow = false) {   /**    * @param target 目標(biāo)對(duì)象    * @param key 需要獲取的值的鍵值    * @param receiver 如果遇到 setter,receiver 則為setter調(diào)用時(shí)的this值    */   return function get(target: Target, key: string | symbol, receiver: object) {     // ReactiveFlags 是在reactive中聲明的枚舉值,如果key是枚舉值則直接返回對(duì)應(yīng)的布爾值     if (key === ReactiveFlags.IS_REACTIVE) {       return !isReadonly     } else if (key === ReactiveFlags.IS_READONLY) {       return isReadonly     } else if (       // 如果key是raw  receiver 指向調(diào)用者,則直接返回目標(biāo)對(duì)象。       // 這里判斷是為了保證觸發(fā)攔截 handle 的是 proxy 本身而不是 proxy 的繼承者       // 觸發(fā)攔的兩種方式:一是訪問(wèn) proxy 對(duì)象本身的屬性,二是訪問(wèn)對(duì)象原型鏈上有 proxy 對(duì)象的對(duì)象的屬性,因?yàn)椴樵儠?huì)沿著原型鏈向下找       key === ReactiveFlags.RAW &&       receiver ===         (isReadonly           ? shallow             ? shallowReadonlyMap             : readonlyMap           : shallow           ? shallowReactiveMap           : reactiveMap         ).get(target)     ) {       return target     }      const targetIsArray = isArray(target)     // 如果目標(biāo)對(duì)象 不為只讀、是數(shù)組、key屬于arrayInstrumentations:['includes', 'indexOf', 'lastIndexOf']方法之一,即觸發(fā)了這三個(gè)方法之一     if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {       // 通過(guò) proxy 調(diào)用,arrayInstrumentations[key]的this一定指向 proxy       return Reflect.get(arrayInstrumentations, key, receiver)     }      const res = Reflect.get(target, key, receiver)      // 如果 key 是 symbol 內(nèi)置方法,或者訪問(wèn)的是原型對(duì)象__proto__,直接返回結(jié)果,不收集依賴     if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {       return res     }      // 不是只讀類型的 target 就收集依賴。因?yàn)橹蛔x類型不會(huì)變化,無(wú)法觸發(fā) setter,也就會(huì)觸發(fā)更新     if (!isReadonly) {       track(target, TrackOpTypes.GET, key)     }      // 如果是淺觀察,不做遞歸轉(zhuǎn)化,就是說(shuō)對(duì)象有屬性值還是對(duì)象的話不遞歸調(diào)用 reactive()     if (shallow) {       return res     }      // 如果get的結(jié)果是ref     if (isRef(res)) {       // ref unwrapping - does not apply for Array + integer key.       // 返回 ref.value,數(shù)組除外       const shouldUnwrap = !targetIsArray || !isIntegerKey(key)       return shouldUnwrap ? res.value : res     }      // 由于 proxy 只能代理一層,如果子元素是對(duì)象,需要遞歸繼續(xù)代理     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   } }

      看著長(zhǎng),最終就是track()依賴收集

      track()依賴收集內(nèi)容過(guò)多,和trigger()觸發(fā)更新一起,單開(kāi)一篇文章

      • createSetter()

      /**  * 攔截對(duì)象的設(shè)置屬性操作  * @param shallow 是否是淺觀察  * @returns   */ function createSetter(shallow = false) {   /**    * @param target 目標(biāo)對(duì)象    * @param key 設(shè)置的屬性名稱    * @param value 要改變的屬性值    * @param receiver 如果遇到setter,receiver則為setter調(diào)用時(shí)的this值    */   return function set(     target: object,     key: string | symbol,     value: unknown,     receiver: object   ): boolean {     let oldValue = (target as any)[key]     // 如果模式不是淺觀察模式     if (!shallow) {       // 拿新值和老值的原始值,因?yàn)樾聜魅氲闹悼赡苁琼憫?yīng)式數(shù)據(jù),如果直接和 target 上原始值比較是沒(méi)有意義的       value = toRaw(value)       oldValue = toRaw(oldValue)       // 目標(biāo)對(duì)象不是數(shù)組,舊值是ref,新值不是ref,則直接賦值,這里提到ref       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     }     // 檢查對(duì)象是否有這個(gè)屬性     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     // reactive是proxy實(shí)例才觸發(fā)更新,防止通過(guò)原型鏈觸發(fā)攔截器觸發(fā)更新     if (target === toRaw(receiver)) {       if (!hadKey) {         // 如果不存在則trigger ADD         trigger(target, TriggerOpTypes.ADD, key, value)       } else if (hasChanged(value, oldValue)) {         // 如果新舊值不相等則trigger SET         trigger(target, TriggerOpTypes.SET, key, value, oldValue)       }     }     return result   } }

      trigger()觸發(fā)更新

      ref

      源碼地址:packages/reactivity/src/ref.ts

      接收一個(gè)可選unknown,接著直接調(diào)用createRef()

      export function ref(value?: unknown) {   return createRef(value, false) }

      詳解vue3中reactive和ref的區(qū)別(源碼解析)

      ref的區(qū)別就是在調(diào)用createRef()時(shí)第二個(gè)值傳的是true

      export function shallowRef(value?: unknown) {   return createRef(value, true) }

      看一下官方文檔上對(duì)shallowRef的解釋

      詳解vue3中reactive和ref的區(qū)別(源碼解析)

      createRef

      通過(guò)isRef()判斷是否是ref數(shù)據(jù),是則直接返回該數(shù)據(jù),不是則通過(guò)new RefImpl創(chuàng)建ref數(shù)據(jù)

      在創(chuàng)建時(shí)會(huì)傳兩個(gè)值一個(gè)是rawValue(原始值),一個(gè)是shallow(是否是淺觀察),具體使用場(chǎng)景可看上面refshallowRef的介紹

      function createRef(rawValue: unknown, shallow: boolean) {   // 是否是 ref 數(shù)據(jù)   if (isRef(rawValue)) {     return rawValue   }   return new RefImpl(rawValue, shallow) }
      • isRef()

      通過(guò)__v_isRef只讀屬性判斷是否是ref數(shù)據(jù),此屬性會(huì)在RefImpl創(chuàng)建ref數(shù)據(jù)時(shí)添加

      export function isRef(r: any): r is Ref {   return Boolean(r && r.__v_isRef === true) }

      RefImpl

      class RefImpl<T> {   private _value: T   private _rawValue: T    public dep?: Dep = undefined   // 只讀屬性 __v_isRef 判斷是否是ref數(shù)據(jù)的靜態(tài)標(biāo)識(shí)   public readonly __v_isRef = true    constructor(value: T, public readonly _shallow: boolean) {     this._rawValue = _shallow ? value : toRaw(value)  // 非淺觀察用toRaw()包裹原始值     this._value = _shallow ? value : toReactive(value) // 非淺觀察用toReactive()處理數(shù)據(jù)   }    get value() {   // 依賴收集     trackRefValue(this)     return this._value   }    set value(newVal) {     newVal = this._shallow ? newVal : toRaw(newVal) // 非淺觀察用toRaw()包裹值     // 兩個(gè)值不相等     if (hasChanged(newVal, this._rawValue)) {       this._rawValue = newVal       this._value = this._shallow ? newVal : toReactive(newVal)       triggerRefValue(this, newVal) // 觸發(fā)依賴,派發(fā)更新     }   } }

      根據(jù)RefImpl我們可以看到ref的底層邏輯,如果是對(duì)象確實(shí)會(huì)使用reactive進(jìn)行處理,并且ref的創(chuàng)建使用的也是RefImpl class實(shí)例,value只是RefImpl的屬性

      在我們訪問(wèn)設(shè)置 ref的value值時(shí),也分別是通過(guò)getset攔截進(jìn)行依賴收集派發(fā)更新

      • toReactive

      我們來(lái)看一下toReactive()這個(gè)方法,在RefImpl中創(chuàng)建ref數(shù)據(jù)時(shí)會(huì)調(diào)用toReactive()方法,這里會(huì)先判斷傳進(jìn)來(lái)的值是不是對(duì)象,如果是就用reactive()包裹,否則就返回其本身

      export const toReactive = <T extends unknown>(value: T): T =>   isObject(value) ? reactive(value) : value
      • trackRefValue

      ref的依賴收集方法

      export function trackRefValue(ref: RefBase<any>) {   if (isTracking()) {     ref = toRaw(ref)     if (!ref.dep) {       ref.dep = createDep()     }     if (__DEV__) {       trackEffects(ref.dep, {         target: ref,         type: TrackOpTypes.GET,         key: 'value'       })     } else {       trackEffects(ref.dep)     }   } }
      • triggerRefValue

      ref的派發(fā)更新方法

      export function triggerRefValue(ref: RefBase<any>, newVal?: any) {   ref = toRaw(ref)   if (ref.dep) {     if (__DEV__) {       triggerEffects(ref.dep, {         target: ref,         type: TriggerOpTypes.SET,         key: 'value',         newValue: newVal       })     } else {       triggerEffects(ref.dep)     }   } }

      總結(jié)

      看完reactiveref源碼,相信對(duì)本文一開(kāi)始的幾個(gè)問(wèn)題也都有了答案,這里也總結(jié)了幾個(gè)問(wèn)題:

      • 問(wèn):ref的底層邏輯是什么,具體是如何實(shí)現(xiàn)的

      答:ref底層會(huì)通過(guò) new RefImpl()來(lái)創(chuàng)造ref數(shù)據(jù),在new RefImpl()會(huì)首先給數(shù)據(jù)添加__v_isRef只讀屬性用來(lái)標(biāo)識(shí)ref數(shù)據(jù)。而后判斷傳入的值是否是對(duì)象,如果是對(duì)象則使用toReactive()處理成reactive,并將值賦給RefImpl()value屬性上。在訪問(wèn)設(shè)置ref數(shù)據(jù)的value時(shí)會(huì)分別觸發(fā)依賴收集派發(fā)更新流程。


      • 問(wèn):ref底層是否會(huì)使用reactive處理數(shù)據(jù)

      答:RefImpl中非淺觀察會(huì)調(diào)用toReactive()方法處理數(shù)據(jù),toReactive()中會(huì)先判斷傳入的值是不是一個(gè)對(duì)象,如果是對(duì)象則使用reactive進(jìn)行處理,不是則直接返回值本身。


      • 問(wèn):為什么已經(jīng)有了reactive還需要在設(shè)計(jì)一個(gè)ref呢?

      答: 因?yàn)関ue3響應(yīng)式方案使用的是proxy,而proxy的代理目標(biāo)必須是非原始值,沒(méi)有任何方式能去攔截對(duì)原始值的操作,所以就需要一層對(duì)象作為包裹,間接實(shí)現(xiàn)原始值的響應(yīng)式方案。


      • 問(wèn):為什么ref數(shù)據(jù)必須要有個(gè)value屬性,訪問(wèn)ref數(shù)據(jù)必須要通過(guò).value的方式呢?

      答:這是因?yàn)橐鉀Q響應(yīng)式丟失的問(wèn)題,舉個(gè)例子:

      // obj是響應(yīng)式數(shù)據(jù) const obj = reactive({ foo: 1, bar: 2 })  // newObj 對(duì)象下具有與 obj對(duì)象同名的屬性,并且每個(gè)屬性值都是一個(gè)對(duì)象 // 該對(duì)象具有一個(gè)訪問(wèn)器屬性 value,當(dāng)讀取 value的值時(shí),其實(shí)讀取的是 obj 對(duì)象下相應(yīng)的屬性值  const newObj = {     foo: {         get value() {             return obj.foo         }     },     bar: {         get value() {             return obj.bar         }     } }  effect(() => {     // 在副作用函數(shù)內(nèi)通過(guò)新對(duì)象 newObj 讀取 foo 的屬性值     console.log(newObj.foo) }) // 正常觸發(fā)響應(yīng) obj.foo = 100

      可以看到,在現(xiàn)在的newObj對(duì)象下,具有與obj對(duì)象同名的屬性,而且每個(gè)屬性的值都是一個(gè)對(duì)象,例如foo 屬性的值是:

      {     get value() {         return obj.foo     } }

      該對(duì)象有一個(gè)訪問(wèn)器屬性value,當(dāng)讀取value的值時(shí),最終讀取的是響應(yīng)式數(shù)據(jù)obj下的同名屬性值。也就是說(shuō),當(dāng)在副作用函數(shù)內(nèi)讀取newObj.foo時(shí),等價(jià)于間接讀取了obj.foo的值。這樣響應(yīng)式數(shù)據(jù)就能夠與副作用函數(shù)建立響應(yīng)聯(lián)系

      (學(xué)習(xí)視頻分享:web前端開(kāi)發(fā)、編程基礎(chǔ)視頻)

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)