【VUE】ref 和 reactive 详解及使用场景
ref 和 reactive 详解及使用场景
在 Vue 3 的 Composition API 中,ref 和 reactive 是两种最常用的响应式数据声明方式,但它们的使用场景和特性有所不同。
1. ref
作用
- 用于声明一个响应式的基本类型(如
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)。- 适用于所有数据类型(基本类型 + 引用类型)。
- 可以重新赋值(
ref本身是可变的)。
使用场景
- 基本类型数据(如
boolean、number、string)。 - 需要重新赋值的变量(如切换状态、计数器)。
- 模板直接使用的变量(自动解包)。
示例
<script setup>
import { ref } from 'vue'
const isVisible = ref(false) // 布尔值
const count = ref(0) // 数字
function toggle() {
isVisible.value = !isVisible.value
count.value++
}
</script>
<template>
<button @click="toggle">
{{ isVisible ? 'Hide' : 'Show' }} ({{ count }})
</button>
</template>
2. reactive
作用
- 用于声明一个响应式的对象或数组(仅适用于引用类型)。
- 返回一个 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更简洁)。 - 仅适用于对象/数组(不能用于基本类型)。
- 不能重新赋值(
reactive返回的对象是固定的,不能state = {...})。
使用场景
- 复杂对象或表单数据(如
form、filter数据)。 - 嵌套数据结构的响应式管理(如 API 返回的 JSON 数据)。
- 需要直接修改属性的情况(避免
.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 |
|---|---|---|
| 适用数据类型 | 基本类型 + 引用类型 | 仅对象/数组 |
| 访问方式 | .value(<script>) |
直接访问 |
| 模板自动解包 | ✅(无需 .value) |
✅(直接访问) |
| 重新赋值 | ✅(ref.value = ...) |
❌(不能 obj = {...}) |
| 适用场景 | 基本类型、需要重新赋值的变量 | 复杂对象、表单数据 |
4. 如何选择?
-
用
ref的情况:- 基本类型(
boolean、number、string)。 - 需要重新赋值的变量(如
isLoading = true→isLoading = false)。 - 在
<template>中直接使用(自动解包)。
- 基本类型(
-
用
reactive的情况:- 对象或数组(如
form、filter数据)。 - 需要直接修改属性(避免
.value)。 - 嵌套数据结构(如 API 返回的 JSON)。
- 对象或数组(如
总结
ref:适用于基本类型或需要重新赋值的变量(如isVisible、count)。reactive:适用于复杂对象或表单数据(如form、filterValue)。- 组合使用:Vue 3 推荐混合使用,例如:
const loading = ref(false) // 布尔值 const user = reactive({ name: 'Alice', age: 25 }) // 对象
Vue 3 响应式 API 综合对比(defineModel、defineEmits、ref、reactive)
为了更清晰地区分这四种 API,我们从 用途、语法、适用场景、核心区别 四个方面进行对比,并给出记忆技巧。
1. 核心功能对比
| API | 作用 | 数据类型 | 是否双向绑定 | 是否替代传统写法 |
|---|---|---|---|---|
defineModel |
声明 v-model 绑定的数据 |
任意 | ✅(自动同步父组件) | ✅(替代 props + emit('update:xxx')) |
defineEmits |
声明自定义事件 | - | ❌ | ✅(替代 emits 选项) |
ref |
声明响应式数据(基本/引用类型) | 任意 | ❌(需手动 .value) |
❌(Vue 2 无直接对应) |
reactive |
声明响应式对象/数组 | 仅对象/数组 | ❌ | ❌(Vue 2 类似 data()) |
2. 语法对比
(1)defineModel
const modelValue = defineModel() // 默认 `modelValue`
const username = defineModel("username", { default: "Guest" }) // 自定义 prop 名
- 修改数据:直接赋值(自动同步父组件)
username.value = "Alice" // 触发父组件的 `v-model:username` 更新
(2)defineEmits
const emit = defineEmits(["submit", "cancel"])
- 触发事件:
emit("submit", { data: 123 }) // 父组件监听 @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 |
父子组件双向绑定(如自定义输入框、开关组件) |
defineEmits |
子组件向父组件通信(如提交表单、关闭弹窗) |
ref |
基本类型数据(boolean/number/string)或需要重新赋值的变量 |
reactive |
复杂对象或表单数据(如 form、filter 数据) |
4. 核心区别总结
| 特性 | defineModel |
defineEmits |
ref |
reactive |
|---|---|---|---|---|
| 是否响应式 | ✅ | ❌ | ✅ | ✅ |
是否需要 .value |
✅(<script> 中) |
❌ | ✅(<script> 中) |
❌ |
| 是否支持基本类型 | ✅ | ❌ | ✅ | ❌ |
| 是否支持重新赋值 | ✅ | ❌ | ✅ | ❌(需用 Object.assign) |
| 是否自动同步父组件 | ✅ | ❌ | ❌ | ❌ |
5. 记忆技巧
(1)defineModel vs defineEmits
defineModel:双向数据流(父子组件数据同步)。- 记忆:
v-model的简化版。
- 记忆:
defineEmits:单向事件流(子组件通知父组件)。- 记忆:替代
this.$emit()。
- 记忆:替代
(2)ref vs reactive
ref |
reactive |
|
|---|---|---|
| 数据类型 | 所有类型 | 仅对象/数组 |
| 访问方式 | .value |
直接属性访问 |
| 重新赋值 | ✅(ref.value = ...) |
❌(不能 obj = {...}) |
| 适用场景 | 基本类型、模板变量 | 复杂对象、表单数据 |
记忆口诀:
- “基本用
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. 复杂对象(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>
总结
defineModel:简化v-model双向绑定。defineEmits:声明子组件事件。ref:管理基本类型或需要重新赋值的变量。reactive:管理复杂对象或表单数据。
一句话记忆:
“双向用 defineModel,事件用 defineEmits,简单用 ref,复杂用 reactive” 🚀

浙公网安备 33010602011771号