Vue 3 Composition API 中创建响应式数据的两个核心 API(ref 和 reactive)

Vue3 Composition API 中,ref 和 reactive 是创建响应式数据的核心 API。


ref 可以包装任何类型的值,通过 .value 访问(模板自动解包),适合基本类型和需要重新赋值的变量;


reactive 专为对象/数组设计,实现深层响应式,适合复杂数据结构。


主要区别在于数据类型支持、访问方式和重新赋值能力。


实际开发中,建议基本类型用 ref,复杂对象用 reactive,组合函数返回多值时优先使用 ref 以便解构。


两者性能相当,选择应基于代码组织和开发体验。


refreactiveVue 3 Composition API 中创建响应式数据的两个核心 API


我们来深入解析它们的特点、区别和使用场景。


核心概念

1. reactive - 创建对象/数组的响应式代理

reactive 接收一个普通对象或数组,返回该对象的响应式代理(Proxy)。

javascript 复制代码
import { reactive } from 'vue'

// 创建响应式对象
const state = reactive({
  count: 0,
  user: {
    name: '张三',
    age: 25
  },
  todos: ['学习 Vue', '写代码']
})

// 修改数据会自动触发更新
state.count++  // ✅ 响应式
state.user.name = '李四'  // ✅ 响应式(深层响应)
state.todos.push('新任务')  // ✅ 响应式

特点:

  • 专为对象类型(Object、Array、Map、Set)设计

  • 深层响应式:嵌套对象的所有层级都会变成响应式

  • 返回的是原始对象的 Proxy 代理,修改代理会影响原始对象

  • 直接解构会丢失响应式(需要配合 toRefs


2. ref - 创建任意类型的响应式引用

ref 可以包装任何类型的值,使其变成响应式。

javascript 复制代码
import { ref } from 'vue'

// 创建响应式值
const count = ref(0)  // 包装基本类型
const user = ref({ name: '张三' })  // 包装对象
const list = ref(['a', 'b', 'c'])  // 包装数组

// 访问值需要 .value
console.log(count.value)  // 0
count.value++  // ✅ 响应式更新

// 在模板中自动解包(不需要 .value)
// <div>{{ count }}</div>

特点:

  • 可以包装任何类型的值(String、Number、Boolean、Object、Array等)

  • 返回一个响应式对象 ,只有一个 .value 属性

  • JavaScript 中访问/修改需要 .value

  • 模板中自动解包 ,不需要 .value

  • 对象类型内部其实是调用了 reactive


关键区别对比

特性 reactive ref
数据类型 只接受对象类型(Object、Array等) 接受任何类型
访问方式 直接访问属性 需要 .value(模板中自动解包)
解构响应式 解构会丢失响应式,需用 toRefs 本身就是引用,解构仍响应
重新赋值 不能直接重新赋值整个对象 可以通过 .value 重新赋值
模板使用 直接使用属性名 自动解包,直接使用变量名
适用场景 复杂的状态对象 基本类型、需要重新赋值的响应式变量

常见问题和解决方案

问题1:reactive 解构丢失响应式

javascript 复制代码
const state = reactive({ count: 0, name: 'Vue' })

// ❌ 错误:解构会丢失响应式
const { count, name } = state
count++  // 不会触发更新

// ✅ 正确:使用 toRefs 保持响应式
import { toRefs } from 'vue'
const { count, name } = toRefs(state)
count.value++  // ✅ 保持响应式

问题2:什么时候用 ref,什么时候用 reactive

使用 ref 的场景:
javascript 复制代码
// 1. 基本类型值
const count = ref(0)
const name = ref('张三')
const isLoading = ref(false)

// 2. 需要重新赋值的对象
const user = ref({ name: '张三' })
// 整个替换(reactive 做不到)
user.value = { name: '李四' }

// 3. 模板引用(DOM 元素)
const inputRef = ref(null)
// <input ref="inputRef">

// 4. 函数返回多个响应式值时
function useFeature() {
  const data = ref(null)
  const error = ref(null)
  
  return { data, error }  // 方便解构
}

使用 reactive 的场景:
javascript 复制代码
// 1. 复杂的表单对象
const form = reactive({
  username: '',
  password: '',
  profile: {
    age: 0,
    address: ''
  }
})

// 2. 全局状态管理(类似 Vuex)
const store = reactive({
  user: null,
  token: '',
  isAuthenticated: false
})

// 3. 紧密相关的数据集合
const pagination = reactive({
  page: 1,
  pageSize: 10,
  total: 0
})

问题3:ref 包装对象 vs reactive 直接使用

javascript 复制代码
// 方式1:使用 ref 包装对象
const userRef = ref({ 
  name: '张三',
  profile: { age: 25 }
})

// 访问:userRef.value.name
// 重新赋值:userRef.value = { name: '李四' }

// 方式2:使用 reactive 创建对象
const userReactive = reactive({
  name: '张三',
  profile: { age: 25 }
})

// 访问:userReactive.name
// 重新赋值:不能直接 userReactive = {...}

// 实际效果:两者内部都是 Proxy,都能深层响应
// 主要区别在于 API 使用方式

最佳实践建议

1. 统一风格

在团队中约定一致的使用规则,例如:

  • 表单数据使用 reactive

  • 单个状态值使用 ref

  • 函数返回的多个值使用 ref(方便解构)


2. 组合式函数推荐使用 ref

javascript 复制代码
// 推荐:返回 ref,方便使用者解构
function useCounter() {
  const count = ref(0)
  const increment = () => count.value++
  
  return { count, increment }
}

// 使用
const { count, increment } = useCounter()

3. 使用 TypeScript 时

TypeScript 复制代码
import { ref, reactive } from 'vue'

// ref 可以推断类型
const count = ref<number>(0)  // Ref<number>
const user = ref<User>({ name: '' })  // Ref<User>

// reactive 也可以
const state = reactive<{
  count: number
  user: User | null
}>({
  count: 0,
  user: null
})

4. 实际开发中的常见模式

javascript 复制代码
import { ref, reactive, computed, watch } from 'vue'

export default {
  setup() {
    // 单个状态值
    const loading = ref(false)
    const error = ref(null)
    
    // 复杂状态对象
    const formState = reactive({
      username: '',
      password: '',
      remember: false
    })
    
    // 计算属性(自动追踪依赖)
    const isFormValid = computed(() => {
      return formState.username && formState.password
    })
    
    // 侦听器
    watch(
      () => formState.username,
      (newName) => {
        console.log('用户名变化:', newName)
      }
    )
    
    // 方法
    const submitForm = async () => {
      loading.value = true
      try {
        // 提交逻辑
      } catch (err) {
        error.value = err
      } finally {
        loading.value = false
      }
    }
    
    return {
      loading,
      error,
      formState,
      isFormValid,
      submitForm
    }
  }
}

总结

refreactive 是 Vue 3 响应式系统的基石:

  • ref:更通用,适合包装任何类型的值,特别是基本类型和需要重新赋值的变量

  • reactive:专为对象设计,语法更简洁,适合复杂的嵌套对象


记住一个简单的选择原则:

  • 如果是单个值 (字符串、数字、布尔值) → 用 ref

  • 如果是相关数据的集合 (对象、表单) → 用 reactive

  • 如果不确定或需要灵活性 → 用 ref(因为可以包装对象)


两者在性能上没有本质差异,选择主要基于开发体验和代码组织。随着使用经验的增加,你会自然而然地知道在什么场景下选择哪个更合适。

相关推荐
幽络源小助理2 小时前
SpringBoot+Vue多维分类知识管理系统源码 | Java知识库项目免费下载 – 幽络源
java·vue.js·spring boot
fengyucaihong_1232 小时前
vue加声音播放
javascript·vue.js·ecmascript
华仔啊2 小时前
Vue3 的设计目标是什么?相比 Vue2 做了哪些关键优化?
前端·vue.js
麦麦大数据2 小时前
F066 vue+flask中医草药靶点知识图谱智能问答系统|中医中药医学知识图谱
vue.js·flask·知识图谱·中医·草药·成分知识图谱·靶点
鹏多多2 小时前
前端纯js实现图片模糊和压缩
前端·javascript·vue.js
Aliex_git3 小时前
Vue 2 - 模板编译源码理解
前端·javascript·vue.js·笔记·前端框架
Irene19913 小时前
Vue:Props 和 Emits 对比总结
vue.js·props·emits
saadiya~3 小时前
实战笔记:在 Ubuntu 离线部署 Vue + Nginx 踩坑与避雷指南
vue.js·笔记·nginx
随祥4 小时前
Tauri+vue开发桌面程序环境搭建
前端·javascript·vue.js