第6章:组合式API

第14节:响应式基础

6.14.1 ref函数深度解析

ref是Vue 3组合式API中用于创建响应式数据的核心函数之一。它可以将基本类型或对象类型转换为响应式数据。

基本用法

import { ref } from 'vue'

// 创建基本类型的响应式数据
const count = ref(0)
const message = ref('Hello')
const isActive = ref(true)

// 创建对象类型的响应式数据
const user = ref({
  name: '张三',
  age: 25
})

访问和修改ref数据

使用ref创建的响应式数据需要通过.value属性来访问和修改:

// 访问数据
console.log(count.value) // 0
console.log(message.value) // Hello
console.log(user.value.name) // 张三

// 修改数据
count.value++ // 1
message.value = 'World' // World
user.value.age = 26 // 26

ref的响应式原理

  • 对于基本类型,ref会将其包装成一个包含.value属性的对象
  • 对于对象类型,ref内部会调用reactive函数将其转换为响应式代理
  • 当访问或修改.value属性时,Vue会自动追踪依赖并触发更新

ref在模板中的使用

在模板中使用ref创建的数据时,不需要使用.value,Vue会自动解包:

<template>
  <div>
    <p>count: {{ count }}</p> <!-- 自动解包,不需要.count.value -->
    <button @click="count++">增加</button>
    <p>用户名: {{ user.name }}</p> <!-- 自动解包 -->
    <button @click="user.age++">增加年龄</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const count = ref(0)
const user = ref({ name: '张三', age: 25 })
</script>

6.14.2 reactive函数与响应式对象

reactive函数用于创建对象类型的响应式数据。它返回一个响应式代理对象。

基本用法

import { reactive } from 'vue'

// 创建响应式对象
const state = reactive({
  count: 0,
  message: 'Hello',
  user: {
    name: '张三',
    age: 25
  },
  items: [1, 2, 3]
})

访问和修改reactive数据

使用reactive创建的响应式数据可以直接访问和修改,不需要.value

// 访问数据
console.log(state.count) // 0
console.log(state.user.name) // 张三
console.log(state.items[0]) // 1

// 修改数据
state.count++ // 1
state.message = 'World' // World
state.user.age = 26 // 26
state.items.push(4) // [1, 2, 3, 4]

reactive的响应式原理

  • reactive使用ES6的Proxy API创建响应式代理
  • 自动处理嵌套对象,为所有嵌套属性创建响应式代理
  • 支持数组索引和长度变化的检测
  • 支持新增和删除属性的检测

reactive的局限性

  1. 只支持对象类型:不能用于基本类型(number、string、boolean等)
  2. 直接解构会丢失响应性:直接解构reactive对象会导致失去响应性
  3. 不能替换整个对象:替换整个reactive对象会导致失去响应性

6.14.3 ref vs reactive选择指南

何时使用ref

  1. 基本类型数据ref是创建基本类型响应式数据的唯一选择
  2. 需要解构的数据ref解构后仍能保持响应性
  3. 需要替换整个对象ref可以直接替换.value
  4. 在组合式函数中返回数据ref更适合在组合式函数中返回,因为它可以保持响应性

何时使用reactive

  1. 复杂对象:对于包含多个属性的复杂对象,reactive使用起来更简洁
  2. 不需要解构的数据:如果不需要解构对象,reactive的语法更简洁
  3. 组件内部状态管理:对于组件内部的复杂状态,reactive可以提供更清晰的结构

对比表格

特性 ref reactive
支持的类型 基本类型和对象类型 仅对象类型
访问方式 通过.value访问 直接访问
解构支持 解构后仍保持响应性 直接解构会丢失响应性
替换对象 可以直接替换.value 不能直接替换整个对象
模板使用 自动解包,不需要.value 直接使用
组合式函数返回 推荐使用 不推荐,解构会丢失响应性

示例:ref和reactive的选择

// 基本类型数据,使用ref
const count = ref(0)
const message = ref('Hello')

// 复杂对象,使用reactive
const user = reactive({
  name: '张三',
  age: 25,
  address: {
    city: '北京',
    district: '朝阳区'
  }
})

// 需要解构的数据,使用ref
const config = ref({
  theme: 'light',
  fontSize: 16
})

// 解构后仍保持响应性
const { theme, fontSize } = config.value

6.14.4 toReftoRefs响应式解构

当需要解构reactive对象时,可以使用toReftoRefs函数来保持响应性。

toRef函数

toRef函数用于将reactive对象的单个属性转换为ref

import { reactive, toRef } from 'vue'

const state = reactive({
  count: 0,
  message: 'Hello'
})

// 将单个属性转换为ref
const countRef = toRef(state, 'count')
const messageRef = toRef(state, 'message')

// 访问和修改
console.log(countRef.value) // 0
countRef.value++ // 1
console.log(state.count) // 1 (同步更新)

toRefs函数

toRefs函数用于将reactive对象的所有属性转换为ref,并返回一个新的对象:

import { reactive, toRefs } from 'vue'

const state = reactive({
  count: 0,
  message: 'Hello',
  user: {
    name: '张三',
    age: 25
  }
})

// 将所有属性转换为ref
const refs = toRefs(state)
console.log(refs.count.value) // 0
console.log(refs.message.value) // Hello

// 解构后仍保持响应性
const { count, message, user } = toRefs(state)
console.log(count.value) // 0
count.value++ // 1
console.log(state.count) // 1 (同步更新)

toRef和toRefs的特点

  1. 保持响应性:转换后的ref数据与原reactive对象保持同步
  2. 浅转换toRefs只转换对象的顶层属性,嵌套对象不会被转换
  3. 不创建新数据toReftoRefs不会创建新的数据副本,而是创建对原数据的引用

示例:在组合式函数中使用toRefs

// useCounter.js
import { reactive, toRefs } from 'vue'

export function useCounter() {
  const state = reactive({
    count: 0,
    doubleCount: 0
  })

  function increment() {
    state.count++
    state.doubleCount = state.count * 2
  }

  function decrement() {
    state.count--
    state.doubleCount = state.count * 2
  }

  // 使用toRefs转换后返回,保持响应性
  return {
    ...toRefs(state),
    increment,
    decrement
  }
}

在组件中使用:

<template>
  <div>
    <p>count: {{ count }}</p>
    <p>doubleCount: {{ doubleCount }}</p>
    <button @click="increment">增加</button>
    <button @click="decrement">减少</button>
  </div>
</template>

<script setup>
import { useCounter } from './useCounter'

// 解构后仍保持响应性
const { count, doubleCount, increment, decrement } = useCounter()
</script>

总结

Vue 3的组合式API提供了refreactive两个核心函数来创建响应式数据:

  1. **ref**:适用于基本类型和需要解构的数据,使用.value访问,模板中自动解包
  2. **reactive**:适用于复杂对象,直接访问属性,不能直接解构
  3. **toReftoRefs**:用于将reactive对象的属性转换为ref,保持解构后的响应性

选择使用ref还是reactive取决于具体的使用场景:

  • 对于基本类型,使用ref
  • 对于复杂对象,如果不需要解构,使用reactive
  • 如果需要解构对象,使用ref或者结合toRefs使用reactive

在下一节中,我们将学习组合式API中的计算属性与侦听器。

« 上一篇 JavaScript钩子与列表过渡 下一篇 » 计算属性与侦听器