前言
大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心~
每次有同學學習到
vue3
的時候,總會問我:「
Sunday
老師,
ref
和
reactive
我們應該用哪個呢?」 我告訴他:「我們應該使用
ref
,而不是
reactive
」。那麽此時同學就會有疑惑:「為什麽呢?
ref
還需要
.value
處理,
reactive
看起來會更加簡單呢?」
嗯....每當這個時候,我都需要進行一次長篇大論來解釋這個問題。不過以後應該不需要了,因為這篇文章將會把這個事情解釋的非常清楚......
為什麽推薦使用ref而不是reactive
reactive
在使用過程中存在一些局限性,如果不額外註意這些問題,可能會給開發帶來一些不便。與此不同,
ref
更像是Vue2時代的
option API
中的
data
的替代品,可以存放任何數據型別,而
reactive
聲明的數據型別則僅限於物件。
總體來說,非必要的情況下最好避免使用
reactive
。官方文件也強烈推薦使用
ref()
作為聲明響應式狀態的主要API。以下是詳細原因:
局限性問題:
reactive
本身存在一些局限性,可能會在開發過程中引發一些問題。這需要額外的註意力和處理,否則可能對開發造成麻煩。數據型別限制:
reactive
聲明的數據型別僅限於物件,而ref
則更加靈活,可以容納任何數據型別。這使得ref
更適合一般的響應式狀態的聲明。官方推薦: 官方文件強烈建議使用
ref()
作為聲明響應式狀態的首選。這是因為ref
更簡單、更直觀,同時避免了reactive
可能引發的一些問題。
總的來說:除非有特定的需求需要使用
reactive
,否則在大多數情況下更推薦使用
ref()
。
最懂Vue的人都這麽說了:推薦ref!!!!!!
reactive
和
ref
對比
reactive
|
ref
|
---|---|
❌ 只支持物件和陣列(參照數據型別) | ✅ 支持基本數據型別 + 參照數據型別 |
✅ 在
<script>
和
<template>
中無差別使用
|
❌ 在
<script>
和
<template>
使用方式不同(在
<script>
中要使用
.value
)
|
❌ 重新分配一個新物件會遺失響應性 | ✅ 重新分配一個新物件 不會 失去響應 |
能直接存取內容 |
需要使用
.value
存取內容
|
❌ 將物件傳入函式時,失去響應 | ✅ 傳入函式時,不會失去響應 |
❌ 解構時會遺失響應性,需使用
toRefs
|
❌ 解構物件時會遺失響應性,需使用
toRefs
|
即:
ref
用於將基本型別的數據和參照數據型別(物件)轉換為響應式數據,透過
.value
存取和修改。
reactive
用於將物件轉換為響應式數據,可以直接存取和修改內容,適用於復雜的巢狀物件和陣列。
01:
reactive
有限的值型別
reactive
只能聲明參照數據型別(物件)
let obj = reactive({
name: '小明',
age: 18
})
ref
既能聲明基本數據型別,也能聲明物件和陣列
Vue 提供了
ref()
方法,允許我們建立可以使用
任何值型別
的響應式
ref
。
// 物件
const state = ref({})
// 陣列
const state2 = ref([])
使用
ref
,你可以靈活地聲明基本數據型別、物件或陣列,而不受像
reactive
那樣只能處理參照數據型別的限制。這為開發提供了更大的靈活性,尤其是在處理不同型別的數據時。
02:
reactive
使用不當會失去響應
使用
reactive
時,如果不當使用,可能導致響應性失效,帶來一些困擾。這可能讓開發者在愉快編碼的同時,突然發現某些操作失去了響應性,不明所以。因此,建議在不了解
reactive
失去響應的情況下慎用,而更推薦使用
ref
。
1. 賦值給
reactive
一個整個物件或
reactive
物件
賦值一個普通物件
let state = reactive({ count: 0 })
// 這個賦值將導致 state 失去響應
state = { count: 1 }
賦值一個
reactive
物件
<template>
{{ state }}
</template>
<scriptsetup>
const state = reactive({ count: 0 })
// 在 nextTick 異步方法中修改 state 的值
nextTick(() => {
// 並不會觸發修改 DOM ,說明失去響應了
state = reactive({ count: 11 });
});
</script>
在
nextTick
中給
state
賦值一個
reactive
的響應式物件,但是 DOM 並沒有更新。
解決方法:
不要直接整個物件替換,一個個內容賦值
let state = reactive({ count: 0 })
// state = { count: 1 }
state.count = 1
使用
Object.assign
let state = reactive({ count: 0 })
// state = { count: 1 },state 不會失去響應
state = Object.assign(state, { count: 1 })
使用
ref
定義物件
let state = ref({ count: 0 })
state.value = { count: 1 }
2. 將
reactive
物件的內容賦值給變量(斷開連線/深拷貝)
這種操作類似於深拷貝,不再共享同一記憶體地址,而是只是字面量的賦值,對該變量的賦值不會影響原來物件的內容值。
let state = reactive({ count: 0 })
// 賦值給 n,n 和 state.count 不再共享響應性連線
let n = state.count
// 不影響原始的 state
n++
console.log(state.count) // 0
解決方案:
避免將
reactive
物件的內容賦值給變量。
3. 直接
reactive
物件解構時
直接解構會失去響應。
let state = reactive({ count: 0 })
// 普通解構,count 和 state.count 失去了響應性連線
let { count } = state
count++ // state.count 值依舊是 0
解決方案:
使用
toRefs
解構,解構後的內容是
ref
的響應式變量。
const state = reactive({ count: 0 })
// 使用 toRefs 解構,後的內容為 ref 的響應式變量
let { count } = toRefs(state)
count.value++ // state.count 值改變為 1
建議:
ref
一把梭
推薦使用
ref
,總結原因如下:
reactive
有限的值型別:只能聲明參照數據型別(物件/陣列)。reactive
在一些情況下會失去響應,這可能導致數據回顯失去響應(數據改了,DOM 沒更新)。<template>
{{ state.a }}
{{ state.b }}
{{ state.c }}
</template>
<script>
let state = reactive({ a: 1, b: 2, c: 3 })
onMounted(() => {
// 透過 AJAX 請求獲取的數據,回顯到 reactive,如果處理不好將導致變量失去響應
// 回顯失敗,給響應式數據賦值一個普通物件
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>
給響應式物件的字面量賦一整個普通物件或
reactive
物件將導致
reactive
聲明的響應式數據失去響應。
ref
適用範圍更廣,可聲明基本數據型別和參照數據型別。
雖然使用
ref
聲明的變量在讀取和修改時都需要加
.value
小尾巴,但正因為有這個小尾巴,我們在 review 程式碼的時候就很清楚知道這是一個
ref
聲明的響應式數據。
ref
的
.value
好麻煩!
ref
聲明的響應式變量攜帶迷人的
.value
小尾巴,讓我們一眼就能確定它是一個響應式變量。雖然使用
ref
聲明的變量在讀取和修改時都需要加
.value
小尾巴,但是正因為有這個小尾巴,我們在 review 程式碼的時候就很清楚知道這是一個
ref
聲明的響應式數據。
可能有些人不喜歡這個迷人小尾巴,如果我能自動補全,閣下又如何應對?
Volar 外掛程式能自動補全
.value
(強烈推薦!!!!!)
推薦
ref
一把梭,但是
ref
又得到處
.value
,那就交給外掛程式來完成吧!
Volar
自動補全
.value
(不是預設開啟,需要手動開啟)
reactive
重新賦值遺失響應是因為參照地址變了,被
proxy
代理的物件已經不是原來的那個,所以遺失響應了。其實
ref
也是一樣的,當把
.value
那一層替換成另外一個有著
.value
的物件也會遺失響應。
ref
定義的內容等價於
reactive({ value: xxx })
。
另外,說使用
Object.assign
為什麽可以更新樣版:
Object.assign
解釋是這樣的:如果目標物件與源物件具有相同的鍵(內容名),則目標物件中的內容將被源物件中的內容覆蓋,後面的源物件的內容將類似地覆蓋前面的源物件的同名內容。
那個解決方法裏不用重新賦值,直接
Object.assign(state, { count: 1 })
即可,所以只要
proxy
代理的參照地址沒變,就會一直存在響應性
結語
我是林三心
一個待過 小型toG型外包公司、大型外包公司、小公司、潛力型創業公司、大公司 的作死型前端選手;
一個偏前端的全幹工程師;
一個不正經的金塊作者;
逗比的B站up主;
不帥的小紅書博主;
喜歡打鐵的籃球菜鳥;
喜歡歷史的乏味少年;
喜歡rap的五音不全弱雞如果你想一起學習前端,一起摸魚,一起研究簡歷最佳化,一起研究面試進步,一起交流歷史音樂籃球rap,可以來俺的摸魚學習群哈哈,點這個,有7000多名前端小夥伴在等著一起學習哦 -->
廣州的兄弟可以約飯哦,或者約球~我負責打鐵,你負責進球,謝謝~