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

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

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

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

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

1 : 分析

1.1 ref and reactive 怎么用 ?

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

ref 基本使用

<template>
    <div>
        <span>{{inner.content.text}}</span>
         <span>{{count}}</span>
    </div>
 </template>
<script setup>
const inner = ref({
  content: {
    text: "內部內容"
  }
}); // 你可以通過 ref 定義復雜數據類型
// or
const count = ref(20); //  定義普通數據類型
</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>

 

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

1.2 ref 和 reactive 的區別?

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

那有人就問了 ? reactive 只能存 復雜數據類型嗎?

答案很明顯不是的 reactive也可以存基本數據類型

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

1.3 源碼實現流程 ?

1.3.1 如何找到源碼?

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

1.3.2 : 找到ref函數的源碼文件 ,看看函數內部做了什么事情?

源碼文件 : corepackagesreactivitysrcref.ts

ref.ts

核心代碼實現

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


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

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

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

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

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

 

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

1.3.3.總結一下 ref做了什么?

  1. 調用ref將 定義的數據傳入,返回一個創建ref響應式數據的函數,是否需要淺層復制,默認為false,也就意味著一定會走 toReactive
  2. 調用createRef, 判斷是否 是一個 ref對象 ,是 原值返回 否則 , new 一個 實現ref類
  3. 創建類的私有變量 ,保存傳入的value 和 shallow
  4. 判斷是否淺層復制,如果是則 返回傳入的 value,否則取出 ref的原始值對象
  5. 獲取值的時候將 保存的值 返回 出去
  6. 設置值的時候 判斷當前屬性 是否是淺層對象 ,如果是 則返回該數據 否則 調用 toreactive轉換 reactive 進行操作,如果是 普通數據類型,原值返回,按照 defineProperty 進行處理
  7. 觸發更新

1.3.4 : 找到reactve函數的源碼文件 ,看看函數內部做了什么事情?

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( // 返回一個創建 reactive 的對象
    target, // 傳入 目標對象
    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)) {  // 如果不是一個對象 則 返回當前 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 已經是一個 代理對象 則 返回當前對象
    !(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 // 將對象返回出去
}  

 

1.3.5.總結一下 reactive做了什么?

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

1.4 總結

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

 

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

分享到:
標簽:vue3
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

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

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

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

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定