第六節(jié):帶你全面理解vue3 淺層響應(yīng)式API: shallowRef, shallowReactive, shallowReadonly

前言

前面兩章,給大家講解了vue3ref, reactive,readonly創(chuàng)建響應(yīng)式數(shù)據(jù)的API, 以及常用的計(jì)算屬性computed, 偵聽器watch,watchEffect的使用

其中reactive, ref, readonly創(chuàng)建的響應(yīng)式數(shù)據(jù)都是深層響應(yīng).

而本章主要給大家講解以上三個(gè)API 對(duì)應(yīng)的創(chuàng)建淺層響應(yīng)式數(shù)據(jù)的 API, 即shallowRef, shallowReactive, shallowReadonly

1. shallowRef

shallowRefref淺層作用形式静尼。其實(shí)就是淺層響應(yīng)式

shalloRef的使用方式和ref()完全相同, 不同的只是vue對(duì)數(shù)據(jù)響應(yīng)式處理的不同:

  • ref創(chuàng)建的數(shù)據(jù), 如果有深層, 比如參數(shù)是一個(gè)對(duì)象, 對(duì)象的屬性就是深層, 參數(shù)對(duì)象會(huì)被vue通過(guò)reactive處理為深層響應(yīng)
  • shallowRef創(chuàng)建數(shù)據(jù), 只有頂層的value屬性具有響應(yīng)性, 深層的數(shù)據(jù)不具有響應(yīng)性, 會(huì)原因返回, 即vue不會(huì)遞歸的將深層數(shù)據(jù)通過(guò)reactive處理, 而是原樣存儲(chǔ)和暴露.

1.1. 類型

我們先看一下shallowRefAPI 的類型簽名, 其簽名如下:

function shallowRef<T>(value: T): ShallowRef<T>

interface ShallowRef<T> {
  value: T
}

通過(guò)簽名可以簡(jiǎn)單的看出, shallowRefAPI 接收一個(gè)參數(shù), 返回一個(gè)具有value屬性的對(duì)象, 且value屬性的類型和參數(shù)類型相同.

1.2. ref 和shallowRef 處理原始數(shù)據(jù)類型

接下會(huì)通過(guò)示例帶大家看一下refshallowRef 在處理原始數(shù)據(jù)類型的有什么不同.

示例代碼:

<template>
  <div>
    <h3>shallowRef</h3>
    <div>{{ count }}</div>
    <div>{{ count2 }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, shallowRef } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)
    const count2 = shallowRef(0)

    // 控制臺(tái)輸出 ref, shallowRef 創(chuàng)建的數(shù)據(jù)
    consollog("count", count);
    console.log("count2", count2);

    // 修改數(shù)據(jù)
    const change = () => {
      // count.value++
      count2.value++
    }
    return { count, count2, change }
  }
})
</script>

控制臺(tái)輸出結(jié)果

image.png

通過(guò)代碼的運(yùn)行結(jié)果, 你可以看出,

在參數(shù)為基本數(shù)據(jù)類型的情況下, refshallowRef創(chuàng)建的響應(yīng)式數(shù)據(jù)在使用上完全一樣, value屬性都具有響應(yīng)性

通過(guò)控制臺(tái)輸出結(jié)果, 你會(huì)發(fā)現(xiàn)refshallowRef創(chuàng)建的ref對(duì)象極度相似,
如果你閱讀過(guò)源碼, 你就會(huì)明白, ref, shallowRef返回對(duì)象是通過(guò)同一個(gè)類實(shí)例化的對(duì)象.因此兩個(gè)實(shí)例對(duì)象具有相同的屬性. 但是有一個(gè)屬性__v_isShallow屬性值不同, 因?yàn)?code>vue通過(guò)這個(gè)屬性來(lái)區(qū)分是ref還是shallowRef創(chuàng)建的對(duì)象.

這里不對(duì)源碼實(shí)現(xiàn)做過(guò)多闡述, 如果你對(duì)源碼感興趣, 可以關(guān)注我的vue3源碼專欄


1.3. ref 和shallowRef 處理深層對(duì)象

refshallowRef 在處理深層對(duì)象就會(huì)有所不同:

  • ref函數(shù)在處理深層對(duì)象時(shí), 深層對(duì)象會(huì)被vue自動(dòng)調(diào)用reactive包裹成響應(yīng)式數(shù)據(jù),
  • shallowRef函數(shù)在處理深層對(duì)象時(shí), vue不同將深層對(duì)象包裹為響應(yīng)式對(duì)象, 也就是說(shuō)shallowRef只有.value屬性值才具有響應(yīng)性, 深層對(duì)象不具有響應(yīng)性

示例:

<template>
  <div>
    <h3>shallowRef</h3>
    <div>{{ count }}</div>
    <div>{{ count2 }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, shallowRef } from 'vue'

export default defineComponent({
  setup() {
    const count = ref({ count: 0 })
    const count2 = shallowRef({ count: 0 })

    // 控制輸出 ref, shallowRef 對(duì)于參數(shù)為對(duì)象時(shí)創(chuàng)建的ref 對(duì)象
    console.log('count', count)
    console.log('count2', count2)

    // 修改數(shù)據(jù)
    const change = () => {
      // count.value.count++

      // 不會(huì)觸發(fā)響應(yīng)式
      // count2.value.count++

      // count2 是shallowRef數(shù)據(jù)
      // shallowRef 數(shù)據(jù)只有通過(guò).value 整體修改時(shí)才會(huì)觸發(fā)響應(yīng)式
      count2.value = { count: 3 }
    }
    return { count, count2, change }
  }
})
</script>

控制臺(tái)輸出:

image.png

通過(guò)控制臺(tái)輸出ref, shallowRef創(chuàng)建的響應(yīng)數(shù)據(jù), 以及示例的運(yùn)行結(jié)果, 會(huì)發(fā)現(xiàn):

  • shallowRef在參數(shù)為深層對(duì)象時(shí), 創(chuàng)建的ref數(shù)據(jù), value值就是參數(shù)原對(duì)象, 不具有響應(yīng)性
  • refvalue屬性值, 是vue調(diào)用reactive函數(shù)包裹成的Proxy代理對(duì)象, 即響應(yīng)式數(shù)據(jù)

因此, 你可以理解shallowRefref淺層響應(yīng)式的API, 只有通過(guò).value修改數(shù)據(jù)才會(huì)觸發(fā)響應(yīng)式, 深層對(duì)象沒有通過(guò)reactive包裹, 因此深層操作數(shù)據(jù)不具有響應(yīng)式

shallowRef() 常常用于對(duì)大型數(shù)據(jù)結(jié)構(gòu)的性能優(yōu)化或是與外部的狀態(tài)管理系統(tǒng)集成攻旦。


2. shallowReactive

shallowRefref關(guān)系相似,

shallowReactivereactive淺層作用形式。就是創(chuàng)建具有淺層響應(yīng)性的數(shù)據(jù)


2.1. shallowReactive 類型

首先,我們先看一下shallowReactive類型簽名, 簽名如下:

function shallowReactive<T extends object>(target: T): T

通過(guò)簽名可以看出, shallowReactive函數(shù)接收一個(gè)對(duì)象作為參數(shù), 返回類型與參數(shù)類型相同

2.2. shallowReactive 淺層響應(yīng)式

reactive() 不同帽氓,shallowReactive創(chuàng)建響應(yīng)對(duì)象沒有深層級(jí)的轉(zhuǎn)換:一個(gè)淺層響應(yīng)式對(duì)象里只有根級(jí)別的屬性是響應(yīng)式的, 深層對(duì)象不會(huì)被vue自動(dòng)包裝為響應(yīng)對(duì)象.

示例:

<template>
  <div>
    <h3>shallowReactive</h3>
    <div>{{ user }}</div>
    <div>{{ user2 }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, shallowReactive } from 'vue'

export default defineComponent({
  setup() {
    const user = reactive({ name: '張三', age: 18, friend: { name: '李四' } })
    const user2 = shallowReactive({ name: '張三', age: 18, friend: { name: '李四' } })

    // 控制臺(tái)輸出reactive, shallowReactive 創(chuàng)建的深層對(duì)象
    console.log("reactive friend", user.friend);
    console.log("shallowReactive friend", user2.friend);

    
    // 修改數(shù)據(jù)
    const change = () => {
      // 1. reactive, shallowReactive 在處理第一層對(duì)象屬性時(shí)
      // 都會(huì)觸發(fā)響應(yīng)式
      // user.name = "王五"  // 修改 reactive
      // user2.name = "王五"  // 修改 shallowReactive

      // 2. 操作深度屬性,shallowReactive 不會(huì)觸發(fā)響應(yīng)性
      // user.friend.name = '王五'  // 修改 reactive
      user2.friend.name = '王五'  // 修改 shallowReactive  不觸發(fā)響應(yīng)式

    }
    return { user, user2, change }
  }
})
</script>

控制臺(tái)輸出結(jié)果:

image.png

通過(guò)控制臺(tái)輸出結(jié)果, 你應(yīng)該已經(jīng)看出:

  • reactive創(chuàng)建的響應(yīng)數(shù)據(jù)(代理對(duì)象) 深層對(duì)象也會(huì)自動(dòng)的調(diào)用reactive函數(shù), 創(chuàng)建為響應(yīng)數(shù)據(jù)
  • shallowReactive創(chuàng)建淺層響應(yīng)數(shù)據(jù), 其深層對(duì)象就是原樣的不同對(duì)象, 不具有響應(yīng)性.

運(yùn)行結(jié)果也可以看出, 修改shallowReactive深層對(duì)象的數(shù)據(jù), 頁(yè)面是不會(huì)有任何變化的.因?yàn)椴痪哂许憫?yīng)性.

2.3. shallowReactive 深層ref 數(shù)據(jù)不會(huì)自動(dòng)解包

shallowReactive()函數(shù)創(chuàng)建一個(gè)淺層響應(yīng)式對(duì)象里只有根級(jí)別的屬性是響應(yīng)式的父虑。

也可以說(shuō)shallowReactive()函數(shù)創(chuàng)建的數(shù)據(jù)是非深度監(jiān)聽, 只會(huì)包裝第一個(gè)對(duì)象, 這也就意味著深層的ref數(shù)據(jù)不會(huì)被自動(dòng)解包.

因?yàn)?code>shallowReactive 深層數(shù)據(jù)的存儲(chǔ)是原樣存儲(chǔ), 不會(huì)包裹為深度響應(yīng)式,

示例:

<template>
  <div>
    <h3>shallowReactive</h3>
    <div>{{ user }}</div>
    <div>{{ user2 }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, ref, shallowReactive } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(10)
    const user = reactive({ name: '張三', age: 18, count })
    const count2 = ref(20)
    const user2 = shallowReactive({ name: '張三', age: 18, count: count2 })

    // 修改數(shù)據(jù)
    const change = () => {
      // 1. reactive 操作深層ref 數(shù)據(jù)時(shí),自動(dòng)解包
      // 此時(shí)修改user.count 數(shù)據(jù)時(shí)不用添加.value
      // user.count = 20  // 修改 reactive

      // 2. shallowReactive 操作深層ref 數(shù)據(jù)時(shí),不會(huì)自動(dòng)解包
      // 此時(shí)修改user2.count 數(shù)據(jù)時(shí)必須使用.value
      user2.count.value = 40  // 修改 shallowReactive


    }
    return { user, user2, change }
  }
})
</script>


2.4. shallowReactive與shallowRef 使用比較

  1. 一般情況下使用refreactive即可
  2. 如果有一個(gè)對(duì)象數(shù)據(jù), 結(jié)構(gòu)比較深, 但只有一層對(duì)象的屬性變化會(huì)觸發(fā)響應(yīng), 使用shallowReactive
  3. 如果有一個(gè)對(duì)象數(shù)據(jù), 后面會(huì)產(chǎn)生新的對(duì)象來(lái)整體替換觸發(fā)響應(yīng)式, 使用shallowRef

3. shallowReadonly

shallowReadonlyreadonly淺層作用形式。只有第一層是只讀的,深層不是只讀屬性,

也可以這么理解, 只有第一層的對(duì)象屬性不能修改值, 但深層的數(shù)據(jù)是可以修改的, 是非深層只讀

3.1. shallowReadonly 淺層只讀

shallowReadonly在使用上與readonly完全相同, 區(qū)域在于:

  • readonly() 創(chuàng)建只讀代理, 如果有深層對(duì)象, 深層對(duì)象也會(huì)自動(dòng)調(diào)用readonly處理為只讀代理
  • shallowReadonly 創(chuàng)建的是淺層只讀代理, 也就是深層對(duì)象不會(huì)自動(dòng)調(diào)用readonly包裹, 所以深層對(duì)象是非只讀的, 即可以修改的. 也就意味著只有根層級(jí)的屬性變?yōu)榱酥蛔x。

示例:

<template>
  <div>
    <h3>shallowReadonly</h3>
    <div>{{ user }}</div>
    <div>{{ user2 }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, readonly, ref, shallowReadonly } from 'vue'

export default defineComponent({
  setup() {
    const user = readonly({ name: '張三', age: 18, friend: { name: '李四' } })
    const user2 = shallowReadonly({ name: '張三', age: 18, friend: { name: '李四' } })

    // 修改數(shù)據(jù)
    const change = () => {
      // readonly 為深層只讀, 修改任何一層屬性值都是不合法
      // user.name = '王五'
      // user.friend.name = '王五'


      // shallowReadonly 只有第一層會(huì)被轉(zhuǎn)為只讀, 深層屬性會(huì)原樣存儲(chǔ),不是只讀的
      // user2.name = '王五'  // shallowReadonly 第一層修改報(bào)錯(cuò),因?yàn)槭侵蛔x的
     
      // shallowReadonly 修改生成不會(huì)報(bào)錯(cuò),
      // 但也不會(huì)觸發(fā)響應(yīng)性, 因?yàn)樵瓨哟鎯?chǔ)就是一個(gè)普通對(duì)象
      user2.friend.name = '王五'


    }
    return { user, user2, change }
  }
})
</script>


3.2. shallowReadonly 處理深層ref不會(huì)自動(dòng)解包

因?yàn)?code>shallowReadonly 深層數(shù)據(jù)的存儲(chǔ)是原樣存儲(chǔ), 不會(huì)自動(dòng)調(diào)用shallowReadonly轉(zhuǎn)為只讀, 因此對(duì)于深層的ref 的數(shù)據(jù)不會(huì)被自動(dòng)解包了舆逃。

示例:

<template>
  <div>
    <h3>shallowReadonly</h3>
    <div>{{ user }}</div>
    <div>{{ user2 }}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, readonly, ref, shallowReadonly } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(10)
    const user = readonly({ name: '張三', age: 18, count })
    const count2 = ref(20)
    const user2 = shallowReadonly({ name: '張三', age: 18, count: count2 })

    // readonly 處理深層數(shù)據(jù)為ref 數(shù)據(jù)時(shí), 會(huì)自動(dòng)解包,不用添加.value
    console.log('user.count', user.count)

    // shallowReadonly 獲取深層ref 數(shù)據(jù)時(shí)必須添加.value, 因?yàn)椴粫?huì)自動(dòng)解包
    console.log('user2.count', user2.count.value)

   
    return { user, user2 }
  }
})
</script>


4. 結(jié)語(yǔ)

至此, 就把shallowRef, shallowReactive,shallowReadonly給大家講解完了, 這三個(gè)API使用上還是相對(duì)較少. 大家要理解這些API的使用原理, 工作中根據(jù)情況選擇不同的API .

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市戳粒,隨后出現(xiàn)的幾起案子路狮,更是在濱河造成了極大的恐慌,老刑警劉巖蔚约,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奄妨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡苹祟,警方通過(guò)查閱死者的電腦和手機(jī)砸抛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)树枫,“玉大人直焙,你說(shuō)我怎么就攤上這事⊥派停” “怎么了箕般?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)舔清。 經(jīng)常有香客問(wèn)我丝里,道長(zhǎng),這世上最難降的妖魔是什么体谒? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任杯聚,我火速辦了婚禮,結(jié)果婚禮上抒痒,老公的妹妹穿的比我還像新娘幌绍。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布傀广。 她就那樣靜靜地躺著颁独,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伪冰。 梳的紋絲不亂的頭發(fā)上誓酒,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音贮聂,去河邊找鬼靠柑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吓懈,可吹牛的內(nèi)容都是我干的歼冰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼耻警,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼隔嫡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起甘穿,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤畔勤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后扒磁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡式曲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年妨托,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吝羞。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡兰伤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钧排,到底是詐尸還是另有隱情敦腔,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布恨溜,位于F島的核電站符衔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏糟袁。R本人自食惡果不足惜判族,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望项戴。 院中可真熱鬧形帮,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至合冀,卻和暖如春各薇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背水慨。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工得糜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晰洒。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓朝抖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谍珊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子治宣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容