Vue3 defineModal ref reative 区别及适用场景

297 阅读2分钟

在 Vue 3 中,refreactive 和 defineModel(需配合 <script setup> 使用)是三种常用的响应式 API,它们有不同的适用场景和区别:

1. ref

特点

  • 用于包装 基本类型(string/number/boolean  或 引用类型(object/array ,使其变成响应式。
  • 通过 .value 访问或修改值(在 <template> 中自动解包,无需 .value)。
  • 适用于 独立变量 或需要明确控制响应式更新的场景。

示例

import { ref } from 'vue';

const count = ref(0); // 基本类型
const user = ref({ name: 'Alice' }); // 对象

// 修改值
count.value++;
user.value.name = 'Bob';

适用场景

✅ 基本类型(如数字、字符串、布尔值)
✅ 需要显式控制响应式更新的变量
✅ 需要直接替换整个对象/数组(如 user.value = { name: 'Bob' }

2. reactive

特点

  • 仅适用于 对象或数组,返回一个响应式代理对象。
  • 直接访问/修改属性,无需 .value
  • 如果直接替换整个对象(state = { ... }),会 失去响应性

示例

import { reactive } from 'vue';

const state = reactive({
  count: 0,
  user: { name: 'Alice' }
});

// 修改属性
state.count++;
state.user.name = 'Bob';

适用场景

✅ 复杂对象或嵌套数据(如表单数据、全局状态)
✅ 需要直接修改属性(无需 .value
❌ 不适用于基本类型(如 reactive(0) 会报错)
❌ 不能直接替换整个对象(否则失去响应性)

3. defineModel(Vue 3.4+ 新增)

特点

  • 用于 简化父子组件的双向绑定(v-model ,替代 props + emit 模式。
  • 必须用在 <script setup> 中。
  • 底层仍然是 ref,但语法更简洁。

示例

父组件 (Parent.vue)

<template>
  <Child v-model="count" />
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>

子组件 (Child.vue)

<template>
  <button @click="modelValue++">{{ modelValue }}</button>
</template>

<script setup>
const modelValue = defineModel(); // 默认 `modelValue`
</script>

自定义修饰符

<!-- 子组件 -->
<script setup>
const [model, modifiers] = defineModel({
  set(value) {
    if (modifiers.capitalize) {
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
    return value
  }
})
</script>

<!-- 父组件使用 -->
<Child v-model.capitalize="text" />

多v-model绑定

<!-- 子组件 -->
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>

<!-- 父组件 -->
<Child 
  v-model:firstName="first" 
  v-model:lastName="last" 
/>

带默认值

<script setup>
const model = defineModel({
  default: '默认值'
})
</script>

与组合式API结合

与watch结合

<script setup>
const model = defineModel()

watch(model, (newVal) => {
  console.log('值变化:', newVal)
})
</script>

与自定义逻辑结合

<script setup>
const model = defineModel()

function reset() {
  model.value = ''
}
</script>

实际应用示例

表单输入组件

<!-- TextInput.vue -->
<script setup>
const model = defineModel<string>({
  default: '',
  set(value) {
    // 自动去除首尾空格
    return value.trim()
  }
})
</script>

<template>
  <input 
    v-model="model"
    class="input"
    :placeholder="placeholder"
  >
</template>

自定义选择器

<!-- CustomSelect.vue -->
<script setup>
const model = defineModel<any>()

const options = [
  { value: 'opt1', label: '选项1' },
  { value: 'opt2', label: '选项2' }
]
</script>

<template>
  <select v-model="model">
    <option 
      v-for="opt in options" 
      :value="opt.value"
    >
      {{ opt.label }}
    </option>
  </select>
</template>

适用场景

✅ 需要 父子组件双向绑定(替代 v-model + emit
✅ 简化 props + emit 的代码
❌ 仅适用于 Vue 3.4+ 版本