Vue 3的響應式系統是其最核心的特性之一,主要通過ref
和reactive
這兩個API來實現。本文將詳細介紹這兩個API的使用方法、區別以及最佳實踐。
1. ref()
的基本使用
ref()
用于創建一個響應式的數據引用。它可以包裝任何類型的值,包括基本類型和對象類型。
1.1 基本類型示例
import { ref } from 'vue'// 創建一個ref
const count = ref(0)// 訪問值
console.log(count.value) // 0// 修改值
count.value++
console.log(count.value) // 1// 在模板中使用(不需要.value)
<template><div>{{ count }}</div>
</template>
1.2 對象類型示例
import { ref } from 'vue'const user = ref({name: 'Alice',age: 25
})// 訪問和修改對象屬性
console.log(user.value.name) // 'Alice'
user.value.age = 26// 替換整個對象
user.value = {name: 'Bob',age: 30
}
2. reactive()
的基本使用
reactive()
用于創建一個響應式對象,但只能用于對象類型(包括數組和Map、Set等集合類型)。
reactive()
返回的是一個原始對象的 Proxy
,它和原始對象是不相等的:
const raw = {}
const proxy = reactive(raw)// 代理對象和原始對象不是全等的
console.log(proxy === raw) // false
2.1 基本示例
import { reactive } from 'vue'const state = reactive({user: {name: 'Alice',age: 25},posts: []
})// 直接訪問和修改屬性(不需要.value)
console.log(state.user.name) // 'Alice'
state.user.age = 26
state.posts.push({ id: 1, title: 'Hello Vue 3' })
2.2 數組示例
const list = reactive([1, 2, 3])// 數組方法都會觸發響應式更新
list.push(4)
list.pop()
list[0] = 10
3. ref vs reactive的主要區別
-
使用方式
- ref:需要通過
.value
訪問和修改值(模板中例外) - reactive:直接訪問和修改屬性
- ref:需要通過
-
適用類型
ref
:可以包裝任何類型reactive
:只能用于對象類型ref
返回一個由RefImpl
類構造出來的對象,而reactive
返回一個原始對象的響應式代理Proxy
。
-
嵌套轉換:
// ref的嵌套對象轉換
const user = ref({name: 'Alice',info: { age: 25 }
})
// 結構:{ value: Proxy({ name: 'Alice', info: Proxy({ age: 25 }) }) }// reactive的嵌套對象轉換
const state = reactive({user: {name: 'Alice',info: { age: 25 }}
})
// 結構:Proxy({ user: { name: 'Alice', info: Proxy({ age: 25 }) } })
4. 注意事項
- 響應式丟失情況
import { reactive, toRefs } from 'vue';
const state = reactive({ count: 0 })// ? 錯誤用法:解構后失去響應性
const { count } = state// ? 正確用法:使用計算屬性
const count = computed(() => state.count)// ? 正確用法:使用toRefs
const { count } = toRefs(state); // 此時 count 仍然為響應式
- ref的自動解包
const count = ref(0)
const state = reactive({count // 在reactive對象中會自動解包
})console.log(state.count) // 直接訪問值,不需要.value
- 不能直接替換reactive對象
let state = reactive({ count: 0 })// 上面的 ({ count: 0 }) 引用將不再被追蹤
// (響應性連接已丟失!)
state = reactive({ count: 1 }) // 這將導致錯誤// ? 正確用法:修改屬性值
state.count = 1//? 正確用法:使用 Object.assign 合并對象
Object.assign(state, { count: 1, otherProp: 'value' })//? 正確用法:如果存在替換整個對象的需求,可以考慮使用 ref 代替 reactive:
const state = ref({ count: 0 })
state.value = { count: 1 } // 合法操作
- 在模板中解包
//在模板渲染上下文中,只有頂級的 ref 屬性才會被解包。
//在下面的例子中,count 和 object 是頂級屬性,但 object.id 不是:
const count = ref(0)
const object = { id: ref(1) }<template>
{{ count + 1 }}
<!-- 但下面這個不會:-->
{{ object.id + 1 }}
</template>
//渲染的結果將是 [object Object]1,因為在計算表達式時 object.id 沒有被解包,仍然是一個 ref 對象。為了解決這個問題,我們可以將 id 解構為一個頂級屬性:
<template><!-- 在模板中自動解包 --><div>{{ count }}</div><!-- 在v-bind中也會自動解包 --><input :value="count"><!-- 但在事件處理器中需要.value --><button @click="count.value++">增加</button>
</template>
5. 總結
1. 核心特性對比
特性 | ref | reactive |
---|---|---|
數據類型 | 任何類型 | 僅對象類型 |
訪問方式 | .value(模板中自動解包) | 直接訪問 |
實現方式 | RefImpl類 | Proxy |
解構行為 | 保持響應性 | 失去響應性 |
對象處理 | 內部使用reactive | 直接代理 |
2. 最佳實踐要點
-
類型選擇:
- 基本類型使用
ref()
- 復雜對象使用
reactive()
- 官方建議使用
ref()
作為聲明響應式狀態的主要 API
- 基本類型使用
-
避免常見陷阱:
- 不要解構
reactive
對象 - 不要直接替換
reactive
對象 - 注意在事件處理器中使用
ref
需要.value
- 不要解構
-
性能優化:
- 合理使用
shallowRef
和shallowReactive
- 避免不必要的深層響應式轉換
- 大型數據集合考慮使用
shallowRef
- 合理使用