亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

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

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

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

帶著這個(gè)疑問(wèn) ,我們 接下來(lái)進(jìn)行逐一探討。

1 : 分析

1.1 ref and reactive 怎么用 ?

相信大家都知道在vue3中我們可以通過(guò)一些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)部?jī)?nèi)容"
  }
}); // 你可以通過(guò) 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使用 這里我就不再過(guò)多介紹了。

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

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

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

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

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

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

1.3.1 如何找到源碼?

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

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

源碼文件 : corepackagesreactivitysrcref.ts

ref.ts

核心代碼實(shí)現(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 返回一個(gè) 創(chuàng)建 的方法 createRef 傳入 兩個(gè)值 
/**
 * @param rawValue ref函數(shù)傳入的參數(shù)
 * @param shallow 是否淺復(fù)制
 */
function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) { // 是否是ref對(duì)象 如果是 則 直接返回
    return rawValue
  }
  return new RefImpl(rawValue, shallow) // 否則 創(chuàng)建 ref 對(duì)象 傳入 rawValue shallow
}


// ref.ts 文件第 134行 
class RefImpl<T> { // 創(chuàng)建一個(gè) ref的 實(shí)現(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) {
    // 實(shí)例被 new時(shí) 執(zhí)行 constructor 保存 傳入的值
    this._rawValue = __v_isShallow ? value : toRaw(value) // 是否淺復(fù)制 , 如果時(shí) 則直接返回 傳入的值 否則進(jìn)行 獲取其原始對(duì)象
        this._value = __v_isShallow ? value : toReactive(value) // 是否淺復(fù)制 是 返回原value 否則 轉(zhuǎn)換成 reactive 對(duì)象
  }

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

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

 

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

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

  1. 調(diào)用ref將 定義的數(shù)據(jù)傳入,返回一個(gè)創(chuàng)建ref響應(yīng)式數(shù)據(jù)的函數(shù),是否需要淺層復(fù)制,默認(rèn)為false,也就意味著一定會(huì)走 toReactive
  2. 調(diào)用createRef, 判斷是否 是一個(gè) ref對(duì)象 ,是 原值返回 否則 , new 一個(gè) 實(shí)現(xiàn)ref類
  3. 創(chuàng)建類的私有變量 ,保存?zhèn)魅氲膙alue 和 shallow
  4. 判斷是否淺層復(fù)制,如果是則 返回傳入的 value,否則取出 ref的原始值對(duì)象
  5. 獲取值的時(shí)候?qū)?保存的值 返回 出去
  6. 設(shè)置值的時(shí)候 判斷當(dāng)前屬性 是否是淺層對(duì)象 ,如果是 則返回該數(shù)據(jù) 否則 調(diào)用 toreactive轉(zhuǎn)換 reactive 進(jìn)行操作,如果是 普通數(shù)據(jù)類型,原值返回,按照 defineProperty 進(jìn)行處理
  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)) { // 如果是 只讀的 不允許 寫入 則返回只讀對(duì)象
    return target
  }
  return createReactiveObject( // 返回一個(gè)創(chuàng)建 reactive 的對(duì)象
    target, // 傳入 目標(biāo)對(duì)象
    false, // 是否是只讀對(duì)象 
    mutableHandlers, //提供 get, set, deleteProperty, has, ownKeys 方法
    mutableCollectionHandlers, // 太多了 自己看源碼 
    reactiveMap // 提供一個(gè) weakmap 集合
    
  )
}
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any> 
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) {  // 如果不是一個(gè)對(duì)象 則 返回當(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)是一個(gè) 代理對(duì)象 則 返回當(dāng)前對(duì)象
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  const existingProxy = proxyMap.get(target) // 如果對(duì)象已經(jīng)有了代理對(duì)象 則直接取值 返回 
  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( // 將對(duì)象進(jìn)行代理 
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy) // 設(shè)置目標(biāo)為代理對(duì)象 
  return proxy // 將對(duì)象返回出去
}  

 

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

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

1.4 總結(jié)

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

 

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

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

網(wǎng)友整理

注冊(cè)時(shí)間:

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

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

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

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

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

答題星2018-06-03

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

全階人生考試2018-06-03

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

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

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

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

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

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定