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在模板中自动解包

{{ count }}
import { ref } from 'vue'
const count = ref(0)

reactive直接使用

{{ state.count }}
{{ state.user.name }}
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'Tom'
}
})

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代理整个对象
直接访问属性
适合多属性对象

posted @ 2025-09-04 10:54  yfceshi  阅读(21)  评论(0)    收藏  举报