【VUE】ref 和 reactive 詳解及使用場景
ref 和 reactive 詳解及使用場景
在 Vue 3 的 Composition API 中,ref 和 reactive 是兩種最常用的響應(yīng)式數(shù)據(jù)聲明方式,但它們的使用場景和特性有所不同。
1. ref
作用
- 用于聲明一個響應(yīng)式的基本類型(如
number、string、boolean)或引用類型(如object、array)。 - 返回一個可變的 ref 對象,其值存儲在
.value屬性中。
語法
import { ref } from 'vue'
const count = ref(0) // 基本類型
const user = ref({ name: 'Alice' }) // 引用類型
- 訪問/修改值:
console.log(count.value) // 0 count.value++ // 修改值
特點
.value訪問:在<script>中必須用.value訪問,但在<template>中自動解包(無需.value)。- 適用于所有數(shù)據(jù)類型(基本類型 + 引用類型)。
- 可以重新賦值(
ref本身是可變的)。
使用場景
- 基本類型數(shù)據(jù)(如
boolean、number、string)。 - 需要重新賦值的變量(如切換狀態(tài)、計數(shù)器)。
- 模板直接使用的變量(自動解包)。
示例
<script setup>
import { ref } from 'vue'
const isVisible = ref(false) // 布爾值
const count = ref(0) // 數(shù)字
function toggle() {
isVisible.value = !isVisible.value
count.value++
}
</script>
<template>
<button @click="toggle">
{{ isVisible ? 'Hide' : 'Show' }} ({{ count }})
</button>
</template>
2. reactive
作用
- 用于聲明一個響應(yīng)式的對象或數(shù)組(僅適用于引用類型)。
- 返回一個 Proxy 代理對象,可以直接修改屬性(不需要
.value)。
語法
import { reactive } from 'vue'
const state = reactive({
name: 'Alice',
age: 25,
hobbies: ['reading', 'coding']
})
- 訪問/修改值:
console.log(state.name) // 'Alice' state.age = 26 // 直接修改屬性
特點
- 無需
.value:直接訪問屬性(比ref更簡潔)。 - 僅適用于對象/數(shù)組(不能用于基本類型)。
- 不能重新賦值(
reactive返回的對象是固定的,不能state = {...})。
使用場景
- 復(fù)雜對象或表單數(shù)據(jù)(如
form、filter數(shù)據(jù))。 - 嵌套數(shù)據(jù)結(jié)構(gòu)的響應(yīng)式管理(如 API 返回的 JSON 數(shù)據(jù))。
- 需要直接修改屬性的情況(避免
.value的繁瑣)。
示例
<script setup>
import { reactive } from 'vue'
const form = reactive({
username: '',
password: '',
remember: false
})
function submit() {
console.log(form.username, form.password)
}
</script>
<template>
<input v-model="form.username" placeholder="Username" />
<input v-model="form.password" placeholder="Password" />
<button @click="submit">Submit</button>
</template>
3. ref vs reactive 對比
| 特性 | ref |
reactive |
|---|---|---|
| 適用數(shù)據(jù)類型 | 基本類型 + 引用類型 | 僅對象/數(shù)組 |
| 訪問方式 | .value(<script>) |
直接訪問 |
| 模板自動解包 | ?(無需 .value) |
?(直接訪問) |
| 重新賦值 | ?(ref.value = ...) |
?(不能 obj = {...}) |
| 適用場景 | 基本類型、需要重新賦值的變量 | 復(fù)雜對象、表單數(shù)據(jù) |
4. 如何選擇?
-
用
ref的情況:- 基本類型(
boolean、number、string)。 - 需要重新賦值的變量(如
isLoading = true→isLoading = false)。 - 在
<template>中直接使用(自動解包)。
- 基本類型(
-
用
reactive的情況:- 對象或數(shù)組(如
form、filter數(shù)據(jù))。 - 需要直接修改屬性(避免
.value)。 - 嵌套數(shù)據(jù)結(jié)構(gòu)(如 API 返回的 JSON)。
- 對象或數(shù)組(如
總結(jié)
ref:適用于基本類型或需要重新賦值的變量(如isVisible、count)。reactive:適用于復(fù)雜對象或表單數(shù)據(jù)(如form、filterValue)。- 組合使用:Vue 3 推薦混合使用,例如:
const loading = ref(false) // 布爾值 const user = reactive({ name: 'Alice', age: 25 }) // 對象
Vue 3 響應(yīng)式 API 綜合對比(defineModel、defineEmits、ref、reactive)
為了更清晰地區(qū)分這四種 API,我們從 用途、語法、適用場景、核心區(qū)別 四個方面進行對比,并給出記憶技巧。
1. 核心功能對比
| API | 作用 | 數(shù)據(jù)類型 | 是否雙向綁定 | 是否替代傳統(tǒng)寫法 |
|---|---|---|---|---|
defineModel |
聲明 v-model 綁定的數(shù)據(jù) |
任意 | ?(自動同步父組件) | ?(替代 props + emit('update:xxx')) |
defineEmits |
聲明自定義事件 | - | ? | ?(替代 emits 選項) |
ref |
聲明響應(yīng)式數(shù)據(jù)(基本/引用類型) | 任意 | ?(需手動 .value) |
?(Vue 2 無直接對應(yīng)) |
reactive |
聲明響應(yīng)式對象/數(shù)組 | 僅對象/數(shù)組 | ? | ?(Vue 2 類似 data()) |
2. 語法對比
(1)defineModel
const modelValue = defineModel() // 默認 `modelValue`
const username = defineModel("username", { default: "Guest" }) // 自定義 prop 名
- 修改數(shù)據(jù):直接賦值(自動同步父組件)
username.value = "Alice" // 觸發(fā)父組件的 `v-model:username` 更新
(2)defineEmits
const emit = defineEmits(["submit", "cancel"])
- 觸發(fā)事件:
emit("submit", { data: 123 }) // 父組件監(jiān)聽 @submit
(3)ref
const count = ref(0) // 基本類型
const user = ref({ name: "Alice" }) // 引用類型
- 訪問/修改:
console.log(count.value) // 0 count.value++ // 修改
(4)reactive
const state = reactive({ name: "Alice", age: 25 })
- 訪問/修改:
console.log(state.name) // "Alice" state.age = 26 // 直接修改屬性
3. 適用場景對比
| API | 典型使用場景 |
|---|---|
defineModel |
父子組件雙向綁定(如自定義輸入框、開關(guān)組件) |
defineEmits |
子組件向父組件通信(如提交表單、關(guān)閉彈窗) |
ref |
基本類型數(shù)據(jù)(boolean/number/string)或需要重新賦值的變量 |
reactive |
復(fù)雜對象或表單數(shù)據(jù)(如 form、filter 數(shù)據(jù)) |
4. 核心區(qū)別總結(jié)
| 特性 | defineModel |
defineEmits |
ref |
reactive |
|---|---|---|---|---|
| 是否響應(yīng)式 | ? | ? | ? | ? |
是否需要 .value |
?(<script> 中) |
? | ?(<script> 中) |
? |
| 是否支持基本類型 | ? | ? | ? | ? |
| 是否支持重新賦值 | ? | ? | ? | ?(需用 Object.assign) |
| 是否自動同步父組件 | ? | ? | ? | ? |
5. 記憶技巧
(1)defineModel vs defineEmits
defineModel:雙向數(shù)據(jù)流(父子組件數(shù)據(jù)同步)。- 記憶:
v-model的簡化版。
- 記憶:
defineEmits:單向事件流(子組件通知父組件)。- 記憶:替代
this.$emit()。
- 記憶:替代
(2)ref vs reactive
ref |
reactive |
|
|---|---|---|
| 數(shù)據(jù)類型 | 所有類型 | 僅對象/數(shù)組 |
| 訪問方式 | .value |
直接屬性訪問 |
| 重新賦值 | ?(ref.value = ...) |
?(不能 obj = {...}) |
| 適用場景 | 基本類型、模板變量 | 復(fù)雜對象、表單數(shù)據(jù) |
記憶口訣:
- “基本用
ref,對象用reactive” - “要改
.value,ref不能少”
6. 綜合示例
<script setup>
// 1. 雙向綁定(defineModel)
const username = defineModel("username", { default: "Guest" })
// 2. 事件通信(defineEmits)
const emit = defineEmits(["submit"])
// 3. 基本類型(ref)
const isLoading = ref(false)
// 4. 復(fù)雜對象(reactive)
const form = reactive({
email: "",
password: ""
})
function handleSubmit() {
emit("submit", { username: username.value, ...form })
isLoading.value = true
}
</script>
<template>
<input v-model="username" />
<input v-model="form.email" />
<button @click="handleSubmit" :disabled="isLoading">
{{ isLoading ? "Submitting..." : "Submit" }}
</button>
</template>
總結(jié)
defineModel:簡化v-model雙向綁定。defineEmits:聲明子組件事件。ref:管理基本類型或需要重新賦值的變量。reactive:管理復(fù)雜對象或表單數(shù)據(jù)。
一句話記憶:
“雙向用 defineModel,事件用 defineEmits,簡單用 ref,復(fù)雜用 reactive” ??

浙公網(wǎng)安備 33010602011771號