响应式 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解构保持响应式