📊 API 分类概览
响应式 API 分类:
├── 基础 API
│ ├── ref() # 响应式引用
│ └── reactive() # 响应式对象
│
├── 浅层 API (性能优化)
│ ├── shallowRef() # 浅层 ref
│ └── shallowReactive() # 浅层 reactive
│
├── 只读 API
│ ├── readonly() # 深度只读
│ └── shallowReadonly() # 浅层只读
│
├── 转换工具
│ ├── toRef() # 属性转 ref
│ ├── toRefs() # 对象转 refs
│ ├── toRaw() # 获取原始对象
│ └── markRaw() # 标记为非响应式
│
└── 检查工具
├── isRef() # 检查 ref
├── isReactive() # 检查 reactive
├── isReadonly() # 检查 readonly
├── isProxy() # 检查代理
└── unref() # 解包 ref
🎯 基础响应式 API
1. ref() - 响应式引用
import { ref } from 'vue'
// 创建响应式引用(任意类型)
const count = ref(0) // Ref<number>
const name = ref('Vue') // Ref<string>
const user = ref({ id: 1 }) // Ref<object>
// 访问和修改
console.log(count.value) // 0
count.value = 1 // 响应式更新
// 模板中自动解包
// <div>{{ count }}</div> // 不需要 .value
// TypeScript 类型
import type { Ref } from 'vue'
const count: Ref<number> = ref(0)
使用场景:
- 基本类型数据(字符串、数字、布尔值)
- DOM 引用(模板引用)
- 需要明确
.value访问的响应式值
2. reactive() - 响应式对象
import { reactive } from 'vue'
// 创建响应式对象(只能是对象类型)
const state = reactive({
count: 0,
user: {
name: 'Alice',
age: 25
},
items: ['a', 'b', 'c']
})
// 直接访问属性(不需要 .value)
console.log(state.count) // 0
state.count = 1 // 响应式更新
state.user.name = 'Bob' // 嵌套属性也是响应式的
// 但不能替换整个对象
// state = { count: 2 } // ❌ 错误
// TypeScript 自动推断类型
interface State {
count: number
user: { name: string; age: number }
}
const state: State = reactive({ count: 0, user: { name: 'A', age: 20 } })
使用场景:
- 表单对象
- 复杂的状态对象
- 嵌套数据结构
⚡ 浅层响应式 API(性能优化)
3. shallowRef() - 浅层响应式引用
import { shallowRef, triggerRef } from 'vue'
// 创建浅层 ref(仅对 .value 替换响应式)
const obj = shallowRef({ count: 0, nested: { value: 1 } })
// 替换整个对象会触发更新
obj.value = { count: 1, nested: { value: 1 } } // ✅ 触发更新
// 修改嵌套属性不会触发更新
obj.value.count = 2 // ❌ 不会触发更新
obj.value.nested.value = 3 // ❌ 不会触发更新
// 手动触发更新
triggerRef(obj) // ✅ 强制触发更新
// 使用场景:大对象或频繁更新的数据
const largeData = shallowRef({
// 包含大量数据的对象
items: Array(10000).fill({ /* ... */ }),
metadata: { /* ... */ }
})
使用场景:
- 大型数据集
- 第三方库对象(不需要 Vue 追踪)
- 频繁更新的嵌套对象
4. shallowReactive() - 浅层响应式对象
import { shallowReactive } from 'vue'
// 创建浅层 reactive(仅根级别属性响应式)
const state = shallowReactive({
count: 0,
user: { // 嵌套对象不是响应式的
name: 'Alice',
profile: { age: 25 } // 深度嵌套也不响应
}
})
// 根级别属性修改会触发更新
state.count = 1 // ✅ 触发更新
state.user = { name: 'Bob' } // ✅ 触发更新(替换整个 user)
// 嵌套属性修改不会触发更新
state.user.name = 'Charlie' // ❌ 不会触发更新
state.user.profile.age = 26 // ❌ 不会触发更新
// 使用场景:配置对象或不需要深度观察的对象
const config = shallowReactive({
apiUrl: 'https://api.example.com',
headers: { // 通常 headers 配置不变
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
timeout: 5000
})
使用场景:
- 配置对象
- 包含大量嵌套数据的对象
- 不希望深度观察的对象
🔧 转换工具 API
5. toRef() - 将属性转换为 ref
import { reactive, toRef } from 'vue'
const state = reactive({
count: 0,
name: 'Vue'
})
// 将 reactive 的属性转换为独立的 ref
const countRef = toRef(state, 'count')
const nameRef = toRef(state, 'name')
// 修改 ref 会更新原始对象
countRef.value++ // state.count 变为 1
console.log(state.count) // 1
// 也可以用于 props
const props = defineProps({
id: { type: Number, required: true }
})
const idRef = toRef(props, 'id') // 保持响应式的 props 引用
使用场景:
- 从 reactive 或 props 中提取单个属性
- 保持属性的响应式引用
6. toRefs() - 将对象的所有属性转换为 refs
import { reactive, toRefs } from 'vue'
const state = reactive({
count: 0,
name: 'Vue',
user: { id: 1 }
})
// 将所有属性转换为 ref
const refs = toRefs(state)
// refs = { count: Ref<0>, name: Ref<'Vue'>, user: Ref<{id: 1}> }
// 可以在不丢失响应式的情况下解构
const { count, name } = toRefs(state)
// 修改 ref 会更新原始对象
count.value++ // state.count 变为 1
// 在组合式函数中非常有用
function useFeature() {
const state = reactive({ x: 0, y: 0 })
const update = () => { state.x++; state.y++ }
return { ...toRefs(state), update } // 返回可解构的响应式数据
}
// 使用
const { x, y, update } = useFeature()
使用场景:
- 组合式函数的返回值
- 解构 reactive 对象
- 模板中直接使用解构的属性
7. unref() - 解包 ref 或返回原始值
import { ref, unref } from 'vue'
const count = ref(0)
const plain = 42
// 如果参数是 ref,返回 .value
console.log(unref(count)) // 0
console.log(unref(plain)) // 42(如果不是 ref,直接返回)
// 等同于
const value = isRef(val) ? val.value : val
// 实用场景:编写接受 ref 或原始值的函数
function double(value) {
return unref(value) * 2
}
console.log(double(count)) // 0 * 2 = 0
console.log(double(10)) // 10 * 2 = 20
// 在计算属性中使用
import { computed } from 'vue'
const doubleCount = computed(() => unref(count) * 2)
使用场景:
- 编写灵活的函数(可接受 ref 或原始值)
- 简化 ref 的访问
- 工具函数中处理可能的 ref 值
🔍 检查工具 API
8. isRef() - 检查是否为 ref
import { ref, reactive, isRef } from 'vue'
const r = ref(0)
const obj = reactive({ count: 0 })
const plain = 42
console.log(isRef(r)) // true
console.log(isRef(obj.count)) // false
console.log(isRef(plain)) // false
// 实际应用:安全地访问 ref
function getValue(value) {
if (isRef(value)) {
return value.value
}
return value
}
// 或使用 unref 简化
function getValue(value) {
return unref(value)
}
9. isReactive() - 检查是否为 reactive 对象
import { reactive, readonly, ref, isReactive } from 'vue'
const state = reactive({ count: 0 })
const roState = readonly(state)
const r = ref({ count: 0 })
const shallow = shallowReactive({ count: 0 })
console.log(isReactive(state)) // true
console.log(isReactive(roState)) // true(只读的也是 reactive)
console.log(isReactive(r.value)) // false(ref 包装的对象)
console.log(isReactive(shallow)) // true(浅层也是 reactive)
// 检查 ref 内部的值
console.log(isReactive(unref(r))) // false(ref 解包后是普通对象)
10. isReadonly() - 检查是否为只读对象
import { reactive, readonly, ref, isReadonly } from 'vue'
const state = reactive({ count: 0 })
const roState = readonly(state)
const roRef = readonly(ref(0))
const plain = { count: 0 }
console.log(isReadonly(state)) // false
console.log(isReadonly(roState)) // true
console.log(isReadonly(roRef)) // true(只读 ref)
console.log(isReadonly(plain)) // false
// 实际应用:防止意外修改
function safeUpdate(obj, key, value) {
if (isReadonly(obj)) {
console.warn('Cannot update readonly object')
return
}
obj[key] = value
}
11. isProxy() - 检查是否为代理对象
import { reactive, readonly, ref, isProxy } from 'vue'
const state = reactive({ count: 0 })
const roState = readonly(state)
const r = ref(0)
const plain = { count: 0 }
console.log(isProxy(state)) // true
console.log(isProxy(roState)) // true
console.log(isProxy(r)) // false(ref 不是 Proxy)
console.log(isProxy(plain)) // false
// isProxy 检查是否是 reactive、readonly、shallowReactive、shallowReadonly
const shallow = shallowReactive({})
const shallowRo = shallowReadonly({})
console.log(isProxy(shallow)) // true
console.log(isProxy(shallowRo)) // true
🎨 实际应用模式
模式1:安全的数据访问器
import { unref, isRef, isReactive, isReadonly } from 'vue'
// 安全的属性访问器
export function getSafeValue(source, key) {
// 解包可能的 ref
const obj = unref(source)
if (!obj || typeof obj !== 'object') {
return undefined
}
// 检查是否可写
if (isReadonly(obj)) {
console.warn(`Attempting to read from readonly object: ${key}`)
}
return obj[key]
}
// 安全的属性设置器
export function setSafeValue(source, key, value) {
const obj = unref(source)
if (!obj || typeof obj !== 'object') {
throw new Error('Source is not an object')
}
if (isReadonly(obj)) {
throw new Error('Cannot modify readonly object')
}
obj[key] = value
return true
}
模式2:响应式数据验证器
import {
ref, reactive,
isRef, isReactive,
unref, toRefs
} from 'vue'
// 验证并标准化响应式数据
export function normalizeReactiveData(input) {
// 如果是 ref,返回解包后的值
if (isRef(input)) {
console.log('Input is a ref, unwrapping...')
return unref(input)
}
// 如果是 reactive,转换为普通对象(失去响应式)
if (isReactive(input)) {
console.log('Input is reactive, converting to plain object...')
return { ...input }
}
// 否则直接返回
return input
}
// 创建安全的响应式状态
export function createSafeState(initialState) {
// 深度响应式
const state = reactive({
data: initialState,
lastUpdated: null,
error: null
})
// 返回只读接口和更新方法
return {
// 只读数据
get data() {
return readonly(state.data)
},
get lastUpdated() {
return state.lastUpdated
},
get error() {
return state.error
},
// 更新方法
update(updater) {
try {
const result = updater(state.data)
if (result !== undefined) {
state.data = result
}
state.lastUpdated = new Date()
state.error = null
} catch (err) {
state.error = err.message
throw err
}
},
// 重置方法
reset() {
state.data = initialState
state.lastUpdated = null
state.error = null
}
}
}
模式3:组合式函数的响应式处理
import {
ref, reactive,
toRefs, unref,
isRef, isReactive
} from 'vue'
// 通用的响应式参数处理
export function useComposableLogic(input) {
// 处理可能的 ref 输入
const inputValue = unref(input)
// 创建响应式状态
const state = reactive({
processedData: null,
isLoading: false,
error: null
})
// 处理函数
const process = async () => {
state.isLoading = true
state.error = null
try {
// 实际处理逻辑
state.processedData = await someAsyncOperation(inputValue)
} catch (err) {
state.error = err.message
} finally {
state.isLoading = false
}
}
// 返回响应式引用
return {
// 使用 toRefs 保持响应式
...toRefs(state),
process,
// 原始状态的只读视图
get rawState() {
return readonly(state)
}
}
}
📊 API 对比总结表
| API | 输入类型 | 输出类型 | 深度响应式 | 可写 | 主要用途 |
|---|---|---|---|---|---|
ref() | 任意 | Ref<T> | ✅ 深度 | ✅ | 基本类型、任意值包装 |
reactive() | 对象 | T | ✅ 深度 | ✅ | 复杂对象状态 |
shallowRef() | 任意 | Ref<T> | ❌ 浅层 | ✅ | 大型对象、性能优化 |
shallowReactive() | 对象 | T | ❌ 浅层 | ✅ | 配置对象、浅层观察 |
readonly() | 任意 | T | ✅ 深度 | ❌ | 保护数据不被修改 |
toRef() | 对象, 属性名 | Ref<T> | 保持原样 | ✅ | 提取单个属性 |
toRefs() | 对象 | { [K]: Ref<T> } | 保持原样 | ✅ | 解构对象 |
unref() | 任意 | T | - | - | 解包 ref |
isRef() | 任意 | boolean | - | - | 检查 ref |
isReactive() | 任意 | boolean | - | - | 检查 reactive |
isReadonly() | 任意 | boolean | - | - | 检查 readonly |
isProxy() | 任意 | boolean | - | - | 检查代理对象 |
🚀 最佳实践指南
1. 选择正确的 API
// 场景:基本类型值
const count = ref(0) // ✅ 推荐
const count = reactive({ value: 0 }) // ❌ 过度设计
// 场景:复杂对象
const form = reactive({ // ✅ 推荐
name: '',
email: '',
age: null
})
const form = ref({ // ❌ 需要 .value 访问
name: '',
email: ''
})
// 场景:大型嵌套对象
const largeConfig = shallowReactive({ // ✅ 性能优化
// ... 大量嵌套数据
})
2. 正确处理解构
import { reactive, toRefs } from 'vue'
// ❌ 错误:直接解构会丢失响应式
const state = reactive({ x: 0, y: 0 })
const { x, y } = state // x, y 不是响应式的
// ✅ 正确:使用 toRefs 保持响应式
const { x, y } = toRefs(state) // x, y 是响应式的 ref
// ✅ 正确:在组合式函数中
function usePosition() {
const state = reactive({ x: 0, y: 0 })
return { ...toRefs(state) }
}
3. 使用 unref 编写灵活的函数
// ❌ 不灵活:只能接受 ref
function double(refValue) {
return refValue.value * 2
}
// ✅ 灵活:接受 ref 或原始值
function double(value) {
return unref(value) * 2
}
// 都可以使用
double(ref(10)) // 20
double(10) // 20
4. 性能优化技巧
// 1. 使用 shallowRef 处理大对象
const largeData = shallowRef(/* 大数据 */)
// 2. 批量更新避免频繁触发
const items = ref([])
// ❌ 不佳:每次 push 都触发更新
for (let i = 0; i < 1000; i++) {
items.value.push(i)
}
// ✅ 优化:批量更新
items.value = Array.from({ length: 1000 }, (_, i) => i)
// 3. 避免不必要的深度响应式
const config = shallowReactive({
// 不变的配置
})
🔧 调试技巧
检查响应式状态
import {
ref, reactive,
isRef, isReactive,
isProxy, unref
} from 'vue'
function debugReactive(obj, name = 'object') {
console.group(`Debug: ${name}`)
console.log('Value:', unref(obj))
console.log('isRef:', isRef(obj))
console.log('isReactive:', isReactive(unref(obj)))
console.log('isProxy:', isProxy(unref(obj)))
console.groupEnd()
}
// 使用
const state = reactive({ count: 0 })
debugReactive(state, 'state')
通过掌握这些 API,你可以更精细地控制 Vue 的响应式系统,在性能、灵活性和开发体验之间找到最佳平衡。