1. 基本概念
ref
- 用于创建响应式的基本数据类型(如string、number、boolean等)
- 也可以用于对象类型,但会自动用
reactive
包装 - 访问值需要通过
.value
属性
reactive
- 用于创建响应式的对象类型(如Object、Array、Map、Set等)
- 不能用于基本数据类型
- 直接访问属性,无需
.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. 内部实现机制
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. 响应式转换
转换示例
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. 使用场景对比
场景示例
// 场景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. 性能考虑
。