為什么推薦使用ref而不是reactive
reactive
本身具有很大局限性導(dǎo)致使用過程需要額外注意,如果忽視這些問題將對開發(fā)造成不小的麻煩;ref更像是vue2時代option api
的data
的替代,可以存放任何數(shù)據(jù)類型,而reactive
聲明的數(shù)據(jù)類型只能是對象;
先拋出結(jié)論,再詳細說原因:非必要不用reactive
! (官方文檔也有對應(yīng)的推薦)
官方原文:建議使用 ref()
作為聲明響應(yīng)式狀態(tài)的主要 API。
最懂Vue的人都這么說了:推薦ref!!!
reactive和 ref 對比
reactive |
ref |
---|---|
?只支持對象和數(shù)組(引用數(shù)據(jù)類型) | ?支持基本數(shù)據(jù)類型+引用數(shù)據(jù)類型 |
?在 <script> 和 <template> 中無差別使用 |
?在 <script> 和 <template> 使用方式不同(script中要.value ) |
?重新分配一個新對象會丟失響應(yīng)性 | ?重新分配一個新對象不會失去響應(yīng) |
能直接訪問屬性 | 需要使用 .value 訪問屬性 |
?將對象傳入函數(shù)時,失去響應(yīng) | ?傳入函數(shù)時,不會失去響應(yīng) |
?解構(gòu)時會丟失響應(yīng)性,需使用toRefs | ?解構(gòu)對象時會丟失響應(yīng)性,需使用toRefs |
- ref 用于將基本類型的數(shù)據(jù)(如字符串、數(shù)字,布爾值等)和引用數(shù)據(jù)類型(對象) 轉(zhuǎn)換為響應(yīng)式數(shù)據(jù)。使用 ref 定義的數(shù)據(jù)可以通過
.value
屬性訪問和修改。 - reactive 用于將對象轉(zhuǎn)換為響應(yīng)式數(shù)據(jù),包括復(fù)雜的嵌套對象和數(shù)組。使用 reactive 定義的數(shù)據(jù)可以直接訪問和修改屬性。
原因1:reactive有限的值類型
reactive只能聲明引用數(shù)據(jù)類型(對象)
let obj = reactive({
name: '小明',
age : 18
})
ref既能聲明基本數(shù)據(jù)類型,也能聲明對象和數(shù)組;
Vue 提供了一個
ref()
方法來允許我們創(chuàng)建可以使用任何值類型的響應(yīng)式 ref
//對象
const state = ref({})
//數(shù)組
const state2 = ref([])
原因2:reactive使用不當(dāng)會失去響應(yīng):
reactive
一時爽,使用不恰當(dāng)?shù)臅r候失去響應(yīng)淚兩行,開開心心敲代碼過程中,會感嘆!!咦?怎么不行?為什么這么賦值失去響應(yīng)了? 辣雞reactive!!! 我要用 ref ????yyds
1. 給reactive賦一整個普通對象/reactive對象
通常在頁面數(shù)據(jù)回顯時,需要將AJAX請求獲取的對象直接賦值給響應(yīng)式對象,如果操作不當(dāng)就導(dǎo)致
reactive聲明的對象
失去響應(yīng)
-
賦值一個普通對象
let state = reactive({ count: 0 }) //這個賦值將導(dǎo)致state失去響應(yīng) state = {count: 1}
-
賦值一個
reactive
對象如果給reactive的響應(yīng)式對象賦值普通對象會失去響應(yīng),那么給它賦值一個reactive的響應(yīng)式對象不就行了嗎?下面試試看
<template>
{{state}}
</template>
?
<stcirpt setup>
const state = reactive({ count: 0 })
//nextTick異步方法中修改state的值
nextTick(() => {
//并不會觸發(fā)修改DOM ,說明失去響應(yīng)了
state = reactive({ count: 11 });
});
</stcirpt>
在nexTick
中給state
賦值一個reactive的響應(yīng)式對象,但是DOM并沒有更新!
解決方法:
-
不要直接整個對象替換,對象屬性一個個賦值
let state = reactive({ count: 0 }) //state={count:1} state.conut = 1
-
使用
Object.assign
let state = reactive({ count: 0 }) // state = {count:1} state失去響應(yīng) state = Object.assign(state , {count:1})
-
使用ref定義對象
非必要不用reactive
let state = ref({ count: 0 }) state.value={count:1}
為什么同樣是賦值對象ref不會失去響應(yīng)而reactive會?
ref
定義的數(shù)據(jù)(包括對象)時,返回的對象是一個包裝過的簡單值,而不是原始值的引用;
就和對象深拷貝一樣,是將對象屬性值的賦值
reactive
定義數(shù)據(jù)(必須是對象),reactive
返回的對象是對原始對象的引用,而不是簡單值的包裝。
類似對象的淺拷貝,是保存對象的棧地址,無論值怎么變還是指向原來的對象的堆地址;
reactive
就算賦值一個新的對象,reactive
還是指向原來對象堆地址
2.將reactive對象的屬性-賦值給變量(斷開連接/深拷貝)
這種類似深拷貝不共享同一內(nèi)存地址了,只是字面量的賦值;對該變量賦值也不會影響原來對象的屬性值
let state = reactive({ count: 0 })
//賦值
// n 是一個局部變量,同 state.count
// 失去響應(yīng)性連接
let n = state.count
// 不影響原始的 state
n++
console.log(state.count) //0
有人就說了,既然賦值對象的屬性,那我賦值一整個對象不就是淺拷貝了嗎?那不就是上面說的給響應(yīng)式對象的字面量賦一整個普通對象/reactive對象
這種情況嗎?這種是會失去響應(yīng)的
3.直接reactive對象解構(gòu)時
- 直接解構(gòu)會失去響應(yīng)
let state = reactive({ count: 0 })
//普通解構(gòu)count 和 state.count 失去了響應(yīng)性連接
let { count } = state
count++ // state.count值依舊是0
解決方案:
-
使用
toRefs
解構(gòu)不會失去響應(yīng)使用toRefs解構(gòu)后的屬性是
ref
的響應(yīng)式數(shù)據(jù)
const state = reactive({ count: 0 })
//使用toRefs解構(gòu),后的屬性為ref的響應(yīng)式變量
let { count } = toRefs(state)
count.value++ // state.count值改變?yōu)?
建議: ref一把梭
當(dāng)使用reactive時,如果不了解reactive失去響應(yīng)的情況,那么使用reactive會造成很多困擾!
推薦使用ref
總結(jié)原因如下:
-
reactive有限的值類型:只能聲明引用數(shù)據(jù)類型(對象/數(shù)組)
-
reactive在一些情況下會失去響應(yīng),這個情況會導(dǎo)致數(shù)據(jù)回顯失去響應(yīng)(數(shù)據(jù)改了,dom沒更新)
給響應(yīng)式對象的字面量賦一整個普通對象,將會導(dǎo)致reactive聲明的響應(yīng)式數(shù)據(jù)失去響應(yīng)
<template> {{state.a}} {{state.b}} {{state.c}} </template> ? <script> let state = reactive({ a:1,b:2,c:3 }) onMounted(()=>{ //通AJAX請求獲取的數(shù)據(jù),回顯到reactive,如果處理不好將導(dǎo)致變量失去響應(yīng), //回顯失敗,給響應(yīng)式數(shù)據(jù)賦值一個普通對象 state = { a:11,b:22,c:333 } //回顯成功,一個個屬性賦值 state.a = 11 state.b = 22 state.c = 33 }) </script>
上面這個例子如果是使用ref進行聲明,直接賦值即可,不需要將屬性拆分一個個賦值
使用ref替代reactive:
<template> {{state.a}} {{state.b}} {{state.c}} </template> ? <script> let state = ref({ a:1,b:2,c:3 }) onMounted(()=>{ //回顯成功 state.value = { a:11,b:22,c:333 } }) </script>
-
ref適用范圍更大,聲明的數(shù)據(jù)類型.基本數(shù)據(jù)類型和引用數(shù)據(jù)類型都行
雖然使用ref聲明的變量,在讀取和修改時都需要加.value
小尾巴,但是正因為是這個小尾巴,我們review代碼的時候就很清楚知道這是一個ref
聲明的響應(yīng)式數(shù)據(jù);
ref的.value
小尾巴好麻煩!
ref聲明的響應(yīng)式變量攜帶迷人的.value
小尾巴,讓我們一眼就能確定它是一個響應(yīng)式變量!雖然使用ref聲明的變量,在讀取和修改時都需要加.value
小尾巴,但是正因為是這個小尾巴,我們review代碼的時候就很清楚知道這是一個ref聲明的響應(yīng)式數(shù)據(jù);
可能有些人不喜歡這個迷人小尾巴,如果我能自動補全閣下又如何應(yīng)對?
volar插件能自動補全.value
(強烈推薦!!!)
本人推薦ref一把梭,但是ref又得到處
.value
,那就交給插件來完成吧!!!
-
valor
自動補全.value
(不是默認開啟,需要手動開啟) -
不會有人不知道Vue3需要不能使用
vetur
要用valor
替代吧?不會不會吧? (必備volar插件)
可以看到當(dāng)輸入ref聲明的響應(yīng)式變量時,volar插件自動填充.value
那還有啥煩惱呢? 方便!
本文會根據(jù)各位的提問和留言持續(xù)更新;
@ 別罵了_我真的不懂vue 說(總結(jié)挺好的,因此摘抄了):文章來源:http://www.zghlxwxcb.cn/news/detail-731382.html
reactive 重新賦值丟失響應(yīng)是因為引用地址變了,被proxy代理的對象已經(jīng)不是原來那個所以丟失響應(yīng)了,其實ref也是一樣的,當(dāng)把.value那一層替換成另外一個有著.value的對象也會丟失響應(yīng) ref定義的屬性等價于reactive({value:xxx})
另外說使用Object.assign為什么可以更新模板
Object.assign解釋是這樣的: 如果目標對象與源對象具有相同的鍵(屬性名),則目標對象中的屬性將被源對象中的屬性覆蓋,后面的源對象的屬性將類似地覆蓋前面的源對象的同名屬性。
那個解決方法里不用重新賦值,直接Object.assign(state,{count:1})即可,所以只要proxy代理的引用地址沒變,就會一直存在響應(yīng)性文章來源地址http://www.zghlxwxcb.cn/news/detail-731382.html
到了這里,關(guān)于Vue3為什么推薦使用ref而不是reactive的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!