Fork me on GitHub

【VUE】ref 和 reactive 详解及使用场景

refreactive 详解及使用场景

在 Vue 3 的 Composition API 中,refreactive 是两种最常用的响应式数据声明方式,但它们的使用场景和特性有所不同。


1. ref

作用

  • 用于声明一个响应式的基本类型(如 numberstringboolean)或引用类型(如 objectarray
  • 返回一个可变的 ref 对象,其值存储在 .value 属性中。

语法

import { ref } from 'vue'

const count = ref(0) // 基本类型
const user = ref({ name: 'Alice' }) // 引用类型
  • 访问/修改值
    console.log(count.value) // 0
    count.value++ // 修改值
    

特点

  1. .value 访问:在 <script> 中必须用 .value 访问,但在 <template>自动解包(无需 .value)。
  2. 适用于所有数据类型(基本类型 + 引用类型)。
  3. 可以重新赋值ref 本身是可变的)。

使用场景

  • 基本类型数据(如 booleannumberstring)。
  • 需要重新赋值的变量(如切换状态、计数器)。
  • 模板直接使用的变量(自动解包)。

示例

<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 // 直接修改属性
    

特点

  1. 无需 .value:直接访问属性(比 ref 更简洁)。
  2. 仅适用于对象/数组(不能用于基本类型)。
  3. 不能重新赋值reactive 返回的对象是固定的,不能 state = {...})。

使用场景

  • 复杂对象或表单数据(如 formfilter 数据)。
  • 嵌套数据结构的响应式管理(如 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. 如何选择?

  1. ref 的情况

    • 基本类型(booleannumberstring)。
    • 需要重新赋值的变量(如 isLoading = trueisLoading = false)。
    • <template> 中直接使用(自动解包)。
  2. reactive 的情况

    • 对象或数组(如 formfilter 数据)。
    • 需要直接修改属性(避免 .value)。
    • 嵌套数据结构(如 API 返回的 JSON)。

总结

  • ref:适用于基本类型或需要重新赋值的变量(如 isVisiblecount)。
  • reactive:适用于复杂对象或表单数据(如 formfilterValue)。
  • 组合使用:Vue 3 推荐混合使用,例如:
    const loading = ref(false) // 布尔值
    const user = reactive({ name: 'Alice', age: 25 }) // 对象
    

Vue 3 响应式 API 综合对比(defineModeldefineEmitsrefreactive

为了更清晰地区分这四种 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 复杂对象或表单数据(如 formfilter 数据)

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
  • “要改 .valueref 不能少”

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 🚀

posted @ 2025-07-25 14:08  极度恐慌_JG  阅读(357)  评论(0)    收藏  举报