ref、toRef、toRefs 的区别与使用场景

Vue 3 中三种响应式 API 的作用和使用方式

问题

Vue 3 提供了 reftoReftoRefs 三个 API,它们分别解决什么问题?为什么需要这三个 API?

解答

为什么需要 ref

reactive 只能处理对象类型,无法处理基本类型(string、number、boolean)。ref 解决了这个问题。

import { ref, reactive } from 'vue'

// reactive 无法处理基本类型
const count = reactive(0) // ❌ 不会响应式

// ref 可以处理任意类型
const count = ref(0)       // ✅ 响应式
const user = ref({ name: 'Tom' }) // ✅ 对象也可以

// 访问和修改
console.log(count.value) // 0
count.value++

为什么需要 toRef

reactive 对象中解构属性会丢失响应式。toRef 可以为 reactive 对象的某个属性创建一个 ref,保持响应式连接。

import { reactive, toRef } from 'vue'

const state = reactive({
  name: 'Tom',
  age: 20
})

// 直接解构会丢失响应式
let { age } = state
age++ // ❌ state.age 不会变化

// 使用 toRef 保持响应式
const ageRef = toRef(state, 'age')
ageRef.value++ // ✅ state.age 也会变成 21

// 双向绑定:修改原对象,ref 也会更新
state.age = 30
console.log(ageRef.value) // 30

为什么需要 toRefs

toRefstoRef 的批量版本,将 reactive 对象的所有属性转换为 ref。

import { reactive, toRefs } from 'vue'

const state = reactive({
  name: 'Tom',
  age: 20,
  city: 'Beijing'
})

// 批量转换为 ref
const { name, age, city } = toRefs(state)

// 每个属性都是 ref,且保持响应式连接
age.value++ // state.age 变成 21
state.name = 'Jerry' // name.value 变成 'Jerry'

实际应用:composable 函数

toRefs 最常见的用途是在组合式函数中返回响应式数据:

import { reactive, toRefs } from 'vue'

// 封装一个 composable
function useMouse() {
  const state = reactive({
    x: 0,
    y: 0
  })

  const update = (e) => {
    state.x = e.pageX
    state.y = e.pageY
  }

  window.addEventListener('mousemove', update)

  // 返回 toRefs,调用方可以解构使用
  return toRefs(state)
}

// 使用时可以解构,且保持响应式
const { x, y } = useMouse()

三者对比

import { ref, reactive, toRef, toRefs } from 'vue'

// ref:创建独立的响应式引用
const count = ref(0)

// reactive:创建响应式对象
const state = reactive({ a: 1, b: 2 })

// toRef:为 reactive 的单个属性创建 ref
const aRef = toRef(state, 'a')

// toRefs:为 reactive 的所有属性创建 ref
const { a, b } = toRefs(state)

关键点

  • ref 用于创建任意类型的响应式数据,通过 .value 访问
  • reactive 对象解构后会丢失响应式
  • toRef 为 reactive 的单个属性创建 ref,保持双向绑定
  • toRefs 批量转换,常用于 composable 函数的返回值
  • 三者配合使用,解决不同场景下的响应式需求