响应式 API
Vue 3 的响应式工具详解
ref vs reactive
这是最核心的选择问题。
ref
<script setup>
import { ref } from 'vue'
// 基本类型
const count = ref(0)
const message = ref('hello')
// 对象类型也可以
const user = ref({ name: 'Tom', age: 20 })
// 访问值需要 .value
count.value++
console.log(count.value) // 1
</script>
<template>
<!-- 模板中自动解包,不需要 .value -->
<div>{{ count }}</div>
<div>{{ user.name }}</div>
</template>
特点:
- 可以包裹任意类型(基本类型、对象、数组)
- 通过
.value 访问和修改
- 本质是创建一个包含 value 属性的响应式对象
- 推荐全用 ref,减少心智负担
reactive
<script setup>
import { reactive } from 'vue'
// 只能是对象,不能是基本类型
const state = reactive({
count: 0,
user: { name: 'Tom' },
list: [1, 2, 3]
})
// 直接访问,不需要 .value
state.count++
</script>
特点:
- 只能是对象(plain object)
- 解构会失去响应式
- 不能替换整个对象
- 适合组合相关联的状态
什么时候选哪个?
| 场景 | 推荐 |
|---|
| 基本类型 | ref |
| 多个相关状态 | reactive |
| 第三方库实例 | ref + markRaw |
| 函数参数/局部变量 | 不需要响应式 |
性能优化 API
shallowRef
只代理第一层,用于大数据量优化:
import { shallowRef, triggerRef } from 'vue'
// 深层不代理,只监听 .value 的变化
const data = shallowRef({ deep: { nested: 'value' } })
// 这样不会触发更新
data.value.deep.nested = 'new'
// 需要手动触发
data.value = { deep: { nested: 'new' } }
// 或
triggerRef(data)
适用场景:大数据量列表、第三方库实例(如 ECharts)。
shallowReactive
只代理第一层属性:
import { shallowReactive } from 'vue'
const state = shallowReactive({
count: 0,
meta: { tags: [] } // 深层不响应式
})
readonly / shallowReadonly
import { readonly, shallowReadonly } from 'vue'
const state = reactive({ count: 0, user: { name: 'Tom' } })
// 深层只读
const readOnlyState = readonly(state)
readOnlyState.count = 1 // 警告!
// 浅层只读
const shallowReadOnlyState = shallowReadonly(state)
shallowReadOnlyState.user.name = 'Jerry' // 可以,深层不代理
适用场景:Provide 给子组件、Hook 返回值防止意外修改。
工具 API
toRaw
从 Proxy 取回原始对象:
import { toRaw, reactive } from 'vue'
const state = reactive({ count: 0 })
const raw = toRaw(state)
raw === state // false!raw 是原始对象
raw.count = 100
state.count // 还是 0,因为修改的是原始对象
用途:性能逃逸、避免 Proxy 开销、调用第三方库方法。
markRaw
标记对象永不响应式:
import { markRaw, reactive } from 'vue'
const instance = markRaw(new VueRouter())
const state = reactive({
router: instance // 不会被代理
})
适用场景:第三方库实例、包含循环引用的对象、不需要响应式的大数据。
isRef / isReactive / isReadonly
import { ref, reactive, readonly, isRef, isReactive, isReadonly } from 'vue'
const count = ref(0)
const state = reactive({})
const ro = readonly({})
isRef(count) // true
isReactive(state) // true
isReadonly(ro) // true
unref
如果参数是 ref,返回 value,否则直接返回:
import { ref, unref } from 'vue'
const count = ref(0)
unref(count) // 0
unref({ a: 1 }) // { a: 1 }
toRefs / toRef
将 reactive 对象的每个属性转成 ref:
<script setup>
import { reactive, toRefs } from 'vue'
const state = reactive({ count: 0, name: 'Tom' })
// toRefs 返回的对象可以解构,且保持响应式
const { count, name } = toRefs(state)
</script>
应用场景:解构 reactive 后保持响应式。
总结
| API | 作用 |
|---|
| ref | 创建响应式引用,基本类型必备 |
| reactive | 创建响应式对象 |
| shallowRef | 浅层响应式,大数据优化 |
| readonly | 深层只读 |
| toRaw | 获取原始对象 |
| markRaw | 标记非响应式 |
| toRefs | 解构保持响应式 |