前言
前面兩章,給大家講解了vue3
中ref
, 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
shallowRef
是ref
淺層作用形式静尼。其實(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. 類型
我們先看一下shallowRef
API 的類型簽名, 其簽名如下:
function shallowRef<T>(value: T): ShallowRef<T>
interface ShallowRef<T> {
value: T
}
通過(guò)簽名可以簡(jiǎn)單的看出, shallowRef
API 接收一個(gè)參數(shù), 返回一個(gè)具有value
屬性的對(duì)象, 且value
屬性的類型和參數(shù)類型相同.
1.2. ref 和shallowRef 處理原始數(shù)據(jù)類型
接下會(huì)通過(guò)示例帶大家看一下ref
和shallowRef
在處理原始數(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é)果
通過(guò)代碼的運(yùn)行結(jié)果, 你可以看出,
在參數(shù)為基本數(shù)據(jù)類型的情況下, ref
和shallowRef
創(chuàng)建的響應(yīng)式數(shù)據(jù)在使用上完全一樣, value
屬性都具有響應(yīng)性
通過(guò)控制臺(tái)輸出結(jié)果, 你會(huì)發(fā)現(xiàn)ref
和shallowRef
創(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ì)象
ref
和 shallowRef
在處理深層對(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)輸出:
通過(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)性 - 但
ref
的value
屬性值, 是vue
調(diào)用reactive
函數(shù)包裹成的Proxy
代理對(duì)象, 即響應(yīng)式數(shù)據(jù)
因此, 你可以理解shallowRef
是ref
淺層響應(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
和shallowRef
與ref
關(guān)系相似,
shallowReactive
是reactive
淺層作用形式。就是創(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é)果:
通過(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 使用比較
- 一般情況下使用
ref
和reactive
即可 - 如果有一個(gè)對(duì)象數(shù)據(jù), 結(jié)構(gòu)比較深, 但只有一層對(duì)象的屬性變化會(huì)觸發(fā)響應(yīng), 使用
shallowReactive
- 如果有一個(gè)對(duì)象數(shù)據(jù), 后面會(huì)產(chǎn)生新的對(duì)象來(lái)整體替換觸發(fā)響應(yīng)式, 使用
shallowRef
3. shallowReadonly
shallowReadonly
是readonly
淺層作用形式。只有第一層是只讀的,深層不是只讀屬性,
也可以這么理解, 只有第一層的對(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 .