日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

最近有朋友在面試過程中經(jīng)常被問到這么一個問題,vue3 中的ref 和 reactive的區(qū)別在哪里,為什么 要定義兩個API 一個 api不能實現(xiàn) 響應(yīng)式更新嗎??

帶著這個疑問 ,我們 接下來進行逐一探討。

1 : 分析

1.1 ref and reactive 怎么用 ?

相信大家都知道在vue3中我們可以通過一些api去定義響應(yīng)式數(shù)據(jù),比如 ref, reactive ,shallowRef.

ref 基本使用

<template>
    <div>
        <span>{{inner.content.text}}</span>
         <span>{{count}}</span>
    </div>
 </template>
<script setup>
const inner = ref({
  content: {
    text: "內(nèi)部內(nèi)容"
  }
}); // 你可以通過 ref 定義復(fù)雜數(shù)據(jù)類型
// or
const count = ref(20); //  定義普通數(shù)據(jù)類型
</script>

 

reactive 基本使用

<template>
  <div>
    <div>{{wApper.subText}}</div>
    <div v-for="item in list" :key="item.id">{{item.content}}</div>
  </div>
</template>
<script setup>
const wapper = reactive({
  subText: inner
});
const list = reactive([
  {
    id: 1,
    content: "render"
  },
  {
    id: 2,
    content: "render2"
  }
]);
</script>

 

當(dāng)然你還可以配合 computed od watchEffec使用 這里我就不再過多介紹了。

1.2 ref 和 reactive 的區(qū)別?

相信大家讀到這里可以看出 ref 既可以定義基本數(shù)據(jù)類型 也可以 定義復(fù)雜數(shù)據(jù)類型,而reactive只定義復(fù)雜數(shù)據(jù)類型。

那有人就問了 ? reactive 只能存 復(fù)雜數(shù)據(jù)類型嗎?

答案很明顯不是的 reactive也可以存基本數(shù)據(jù)類型

那他們到底區(qū)別在哪里呢? 我想這個時候 從我們開發(fā)者的角度上沒辦法看出本質(zhì)的區(qū)別,無非是定義變量唄,那接下來請隨者我一起進入源碼的是世界。

1.3 源碼實現(xiàn)流程 ?

1.3.1 如何找到源碼?

先回答第一個問題,怎么找源碼,這個需要你對源碼的包非常熟悉 我們可以通過看package.json文件先找到它打包的入口文件,然后再去根據(jù)不同的情況找不同的文件。

1.3.2 : 找到ref函數(shù)的源碼文件 ,看看函數(shù)內(nèi)部做了什么事情?

源碼文件 : corepackagesreactivitysrcref.ts

ref.ts

核心代碼實現(xiàn)

// ref.ts 文件93 行 
export function ref(value?: unknown) {
  return createRef(value, false) //1 : 提供 ref函數(shù) , false 是否淺復(fù)制
}
// ref.ts文件第 127行  
// 調(diào)用 ref 返回一個 創(chuàng)建 的方法 createRef 傳入 兩個值 
/**
 * @param rawValue ref函數(shù)傳入的參數(shù)
 * @param shallow 是否淺復(fù)制
 */
function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) { // 是否是ref對象 如果是 則 直接返回
    return rawValue
  }
  return new RefImpl(rawValue, shallow) // 否則 創(chuàng)建 ref 對象 傳入 rawValue shallow
}


// ref.ts 文件第 134行 
class RefImpl<T> { // 創(chuàng)建一個 ref的 實現(xiàn)類 
  private _value: T // 創(chuàng)建私有的 _value 變量 
  private _rawValue: T // 創(chuàng)建私有的 _rawValue 變量 

  public dep?: Dep = undefined // 是否 dep 
  public readonly __v_isRef = true // 只讀的 屬性 是否是 ref 

  constructor(value: T, public readonly __v_isShallow: boolean) {
    // 實例被 new時 執(zhí)行 constructor 保存 傳入的值
    this._rawValue = __v_isShallow ? value : toRaw(value) // 是否淺復(fù)制 , 如果時 則直接返回 傳入的值 否則進行 獲取其原始對象
        this._value = __v_isShallow ? value : toReactive(value) // 是否淺復(fù)制 是 返回原value 否則 轉(zhuǎn)換成 reactive 對象
  }

  get value() { // 獲取值的時候 直接將 constructor 保存的值 返回 
    trackRefValue(this) // 跟蹤 ref 的 value
    return this._value  // 獲取value 是 返回 _value 對象
  }

  set value(newVal) {// 當(dāng) 設(shè)置值的時候 往下看 
  
  // 是否淺復(fù)制 or 值身上是否有 __v_isShallow 標(biāo)識 or 是否是只讀的 標(biāo)識__v_isReadonly
    const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal); 
    // 如果滿足 則 返回新設(shè)置的值  , 如果不是 則 取出 新值的原始對象 
    newVal = useDirectValue ? newVal : toRaw(newVal)  // 如果 你一個 淺層對象(普通數(shù)據(jù)類型) 則 原值返回 否則 判斷是否能從 代理對象中 取出源值
    if (hasChanged(newVal, this._rawValue)) { // 判斷對象是否發(fā)生 變化 變了向下走  
      this._rawValue = newVal // 將最新值 賦給 _rawValue
      this._value = useDirectValue ? newVal : toReactive(newVal) // 判斷是否是基本數(shù)據(jù)類型  如果 是 則 將最新值返回 否則 繼續(xù)轉(zhuǎn)換 reactive
      triggerRefValue(this, newVal) // 觸發(fā) ref 的 value 值進行監(jiān)聽更新
    }
  }
}
// 判斷是否 是對象 如果是 則 reactive代理 否則 返回 當(dāng)前的value
export const toReactive = <T extends unknown>(value: T): T => 
isObject(value) ? reactive(value) : value

 

以上代碼就是 ref 的核心實現(xiàn) , 相信看來好像源碼也沒有那么難。

1.3.3.總結(jié)一下 ref做了什么?

  1. 調(diào)用ref將 定義的數(shù)據(jù)傳入,返回一個創(chuàng)建ref響應(yīng)式數(shù)據(jù)的函數(shù),是否需要淺層復(fù)制,默認(rèn)為false,也就意味著一定會走 toReactive
  2. 調(diào)用createRef, 判斷是否 是一個 ref對象 ,是 原值返回 否則 , new 一個 實現(xiàn)ref類
  3. 創(chuàng)建類的私有變量 ,保存?zhèn)魅氲膙alue 和 shallow
  4. 判斷是否淺層復(fù)制,如果是則 返回傳入的 value,否則取出 ref的原始值對象
  5. 獲取值的時候?qū)?保存的值 返回 出去
  6. 設(shè)置值的時候 判斷當(dāng)前屬性 是否是淺層對象 ,如果是 則返回該數(shù)據(jù) 否則 調(diào)用 toreactive轉(zhuǎn)換 reactive 進行操作,如果是 普通數(shù)據(jù)類型,原值返回,按照 defineProperty 進行處理
  7. 觸發(fā)更新

1.3.4 : 找到reactve函數(shù)的源碼文件 ,看看函數(shù)內(nèi)部做了什么事情?

reactive.ts

export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (isReadonly(target)) { // 如果是 只讀的 不允許 寫入 則返回只讀對象
    return target
  }
  return createReactiveObject( // 返回一個創(chuàng)建 reactive 的對象
    target, // 傳入 目標(biāo)對象
    false, // 是否是只讀對象 
    mutableHandlers, //提供 get, set, deleteProperty, has, ownKeys 方法
    mutableCollectionHandlers, // 太多了 自己看源碼 
    reactiveMap // 提供一個 weakmap 集合
    
  )
}
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any> 
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) {  // 如果不是一個對象 則 返回當(dāng)前 traget 
    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] && // 如果target 已經(jīng)是一個 代理對象 則 返回當(dāng)前對象
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  const existingProxy = proxyMap.get(target) // 如果對象已經(jīng)有了代理對象 則直接取值 返回 
  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) // 設(shè)置目標(biāo)為代理對象 
  return proxy // 將對象返回出去
}  

 

1.3.5.總結(jié)一下 reactive做了什么?

  1. 調(diào)用reactive方法 傳數(shù)據(jù),判斷 對象是否是只讀對象 如果是 直接返回 無法操作 否則向下繼續(xù)執(zhí)行
  2. 調(diào)用 createReactiveObject 返回一個代理對象
  3. 判斷傳入的值是不是一個對象 如果不是則直接原路返回,否則判斷target 是不是一個 已經(jīng)代理過了的對象
  4. 如果是代理過的對象 原路返回 否則 判斷目標(biāo)對象是否有相應(yīng)代理有直接取出響應(yīng)代理對象 否則 繼續(xù)向下
  5. 針對不同的 值類型處理
  6. 代理整個trarget 我,將當(dāng)前代理的對象設(shè)置到weakmap 中 將代理完的對象返回

1.4 總結(jié)

從源碼的角度來說 ref本身 會判斷是否為 基本數(shù)據(jù)類型 如果是 則是defineProperty 實現(xiàn)的如果是復(fù)雜數(shù)據(jù)類型就會按照 reactive進行處理 ,針對不同的數(shù)據(jù)類型會進行操作,當(dāng)你ref為 對象時會轉(zhuǎn)換 reactive對象 將代理的對象 返回給 _value對象,如果是基本數(shù)據(jù)則會判斷是否需要淺層復(fù)制,不需要則直接返回了。 而 reactive 這邊也會判斷 是不是 基本數(shù)據(jù)類型 是 直接返回 否則就直接將對象進行了代理并返回。相信本篇文章能夠給你帶來一些啟發(fā)。

 

作者:前端小張同學(xué)
鏈接:
https://juejin.cn/post/7263411272892825655

分享到:
標(biāo)簽:vue3
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定