Vue 3中`ref`和`reactive`的区别。

1. 基本概念

ref

  • 用于创建响应式的基本数据类型(如string、number、boolean等)
  • 也可以用于对象类型,但会自动用reactive包装
  • 访问值需要通过.value属性

reactive

  • 用于创建响应式的对象类型(如Object、Array、Map、Set等)
  • 不能用于基本数据类型
  • 直接访问属性,无需.value
响应式数据
ref
reactive
基本类型
对象类型
对象类型
需要.value访问
需要.value访问
直接访问属性

2. 使用示例

ref 示例

import { ref } from 'vue'

// 基本类型
const count = ref<number>(0)
const message = ref<string>('Hello')
const isActive = ref<boolean>(false)

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

// 对象类型
const user = ref<{ name: string; age: number }>({
  name: 'Tom',
  age: 25
})

// 访问对象属性
console.log(user.value.name) // 'Tom'
user.value.age = 26 // 修改属性

reactive 示例

import { reactive } from 'vue'

// 对象类型
const state = reactive({
  count: 0,
  message: 'Hello',
  user: {
    name: 'Tom',
    age: 25
  }
})

// 直接访问和修改
console.log(state.count) // 0
state.count++ // 修改值
state.user.name = 'Jerry' // 修改嵌套属性

// 数组
const list = reactive<number[]>([1, 2, 3])
list.push(4) // 数组方法可以正常使用

3. 内部实现机制

ref实现
RefImpl类
_value属性
get value
set value
reactive实现
Proxy代理
get trap
set trap
其他traps
track依赖收集
trigger触发更新

4. 在模板中的使用

ref在模板中自动解包

<template>
  <!-- ref在模板中自动解包,不需要.value -->
  <div>{{ count }}</div>
  <button @click="count++">增加</button>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const count = ref(0)
</script>

reactive直接使用

<template>
  <!-- reactive直接访问属性 -->
  <div>{{ state.count }}</div>
  <div>{{ state.user.name }}</div>
  <button @click="state.count++">增加</button>
</template>

<script setup lang="ts">
import { reactive } from 'vue'

const state = reactive({
  count: 0,
  user: {
    name: 'Tom'
  }
})
</script>

5. 类型定义

ref的类型定义

import { ref, Ref } from 'vue'

// 自动推断类型
const count = ref(0) // Ref<number>

// 显式指定类型
const name = ref<string>('Tom')

// 复杂类型
interface User {
  id: number
  name: string
  email: string
}

const user = ref<User | null>(null)

// 函数参数类型
function updateCount(countRef: Ref<number>) {
  countRef.value++
}

reactive的类型定义

import { reactive } from 'vue'

// 接口定义
interface State {
  count: number
  users: User[]
  settings: {
    theme: string
    language: string
  }
}

// 使用接口
const state = reactive<State>({
  count: 0,
  users: [],
  settings: {
    theme: 'dark',
    language: 'zh-CN'
  }
})

// 类型会被正确推断
state.count++ // OK
state.users.push({ id: 1, name: 'Tom', email: 'tom@example.com' })

6. 响应式转换

响应式数据转换
toRef
toRefs
toRaw
unref
将reactive对象的属性转为ref
将reactive对象转为多个ref
获取原始对象
获取ref的值或原值

转换示例

import { reactive, toRef, toRefs, toRaw, unref } from 'vue'

const state = reactive({
  count: 0,
  name: 'Tom'
})

// toRef - 创建一个ref,指向reactive对象的某个属性
const countRef = toRef(state, 'count')
countRef.value++ // state.count 也会更新

// toRefs - 将reactive对象的所有属性转为ref
const { count, name } = toRefs(state)
count.value++ // state.count 也会更新

// toRaw - 获取原始对象
const rawState = toRaw(state)

// unref - 获取值(无论是ref还是普通值)
const value1 = unref(countRef) // 如果是ref,返回.value
const value2 = unref(10) // 如果不是ref,直接返回

7. 使用场景对比

基本类型
对象类型
需要整体替换
只修改属性
选择ref还是reactive?
数据类型
使用ref
使用场景
使用ref
使用reactive
示例: count, flag, message
示例: userInfo, currentItem
示例: formData, tableData

场景示例

// 场景1:基本类型 - 使用ref
const isLoading = ref(false)
const currentPage = ref(1)
const searchText = ref('')

// 场景2:需要整体替换的对象 - 使用ref
const currentUser = ref<User | null>(null)
// 可以整体替换
currentUser.value = { id: 1, name: 'Tom', email: 'tom@example.com' }
currentUser.value = null

// 场景3:表单数据 - 使用reactive
const formData = reactive({
  username: '',
  password: '',
  remember: false
})
// 通常只修改属性
formData.username = 'admin'

// 场景4:复杂状态管理 - 使用reactive
const appState = reactive({
  user: null,
  permissions: [],
  settings: {
    theme: 'light',
    language: 'en'
  }
})

8. 注意事项和陷阱

ref的注意事项

// 1. 在函数中传递ref
function useCounter() {
  const count = ref(0)
  
  function increment() {
    count.value++ // 需要.value
  }
  
  return { count, increment }
}

// 2. ref解构会失去响应性
const { count } = useCounter()
// count 现在是一个普通数字,不是ref

// 3. 嵌套的ref会自动解包
const nested = ref({
  count: ref(0)
})
// 访问时:nested.value.count,不需要nested.value.count.value

reactive的注意事项

// 1. 不能解构
const state = reactive({ count: 0, name: 'Tom' })
const { count, name } = state // ❌ 失去响应性

// 2. 不能整体赋值
let state = reactive({ count: 0 })
state = reactive({ count: 1 }) // ❌ 失去响应性
//如果想要修改整体,可以用
Object.assign(persion,{count:0})
//注意这个修改地址值没有改变,仅仅只是修改了里面的内容

// 3. 对基本类型无效
const count = reactive(0) // ❌ 会报错

9. 性能考虑

性能对比
ref
reactive
额外的RefImpl包装
访问需要.value
适合单个值
Proxy代理整个对象
直接访问属性
适合多属性对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值