Vue3 响应式系统:`ref`/`reactive`/`watchEffect` 的工作方式与最佳实践

你写 Vue3 时经常会遇到这些"似懂非懂"的问题:

  • 为什么 ref 需要 .value
  • 为什么 reactive 解构后就"不响应"了?
  • watchwatchEffectcomputed 到底怎么选?

这篇不从源码堆概念,而是用"你在项目里一定会踩的坑"为主线,把 Vue3 响应式讲清楚。


1. 响应式的目标是什么

目标只有一个:

  • 当某个状态发生变化时,能定位到"谁用到了它",并触发对应的更新。

对应两个关键动作:

  • 依赖收集:谁在读取这个状态?
  • 派发更新:这个状态变了,要通知谁重新执行?

Vue3 用 Proxy/Reflect + effect(副作用函数)完成这件事。


2. reactive:对象的响应式

reactive(obj) 返回一个 Proxy:

  • 读取属性(get)时:收集依赖
  • 修改属性(set)时:触发更新

适合:

  • 状态本身是一个对象/数组
  • 需要深层属性自然响应

2.1 常见坑:解构丢响应

js 复制代码
const state = reactive({ count: 0 })
const { count } = state
count++ // 这里不会触发视图更新

原因:解构得到的是一个普通值,不再经过 Proxy 的 get

解决:

  • toRefs(state) / toRef(state, 'count')
js 复制代码
import { reactive, toRefs } from 'vue'

const state = reactive({ count: 0 })
const { count } = toRefs(state)
count.value++

3. ref:单值/原始类型的响应式

ref(0) 返回形如 { value: 0 } 的对象,它本身也会被响应式处理。

为什么要有 .value

  • 为了让原始类型也能用同一套"依赖收集/触发更新"的机制

适合:

  • 数字/字符串/布尔值等原始类型
  • 你希望"一个变量就是一个状态"

3.1 ref 包对象会怎样

js 复制代码
const user = ref({ name: 'a' })
user.value.name = 'b' // 也能响应

本质上:ref 内部会把对象也做成响应式(类似 reactive)。


4. computed:带缓存的派生状态

特点:

  • 懒执行:没人读就不算
  • 有缓存:依赖不变不重新算

适合:

  • 由多个状态推导出的展示字段
  • 避免把"计算逻辑"散落在模板里

5. watch vs watchEffect

5.1 watch

  • 你明确知道要监听谁(source)
  • 你需要 old/new
  • 你需要精细控制(immediate / deep / flush)
js 复制代码
watch(
  () => state.keyword,
  (newVal, oldVal) => {
    fetchList(newVal)
  },
  { immediate: true }
)

5.2 watchEffect

  • 你不想声明 source
  • 只要 effect 内读取到的响应式数据变化,就重新执行
js 复制代码
watchEffect(() => {
  fetchList(state.keyword)
})

注意:watchEffect 很方便,但也更容易"无意间读到多余依赖"导致频繁触发。


6. 实战建议(项目里最实用的选择策略)

  • 状态是对象/表单 :优先 reactive,配合 toRefs 暴露字段
  • 状态是单值 :优先 ref
  • 派生展示字段 :用 computed
  • 请求联动 :优先 watch(能控粒度、能拿 old/new、好排查)
  • 自动收集依赖的副作用 :再用 watchEffect

7. 常见坑与排查

  • 响应式失效 :优先检查"是否解构了 reactive"
  • watch 不触发 :检查 source 是否是 getter(watch(() => obj.x, ...)
  • 频繁触发 :检查 watchEffect 内是否读取了多余状态

8. 总结

  • Vue3 响应式核心是:依赖收集 + 派发更新
  • reactive 适合对象,但解构会丢响应,需要 toRefs
  • ref 让原始类型也能响应式
  • computed 做派生字段,带缓存
  • watch 更可控,watchEffect 更省事但更容易过度依赖
相关推荐
灵感__idea38 分钟前
Hello 算法:贪心的世界
前端·javascript·算法
GreenTea2 小时前
一文搞懂Harness Engineering与Meta-Harness
前端·人工智能·后端
killerbasd4 小时前
牧苏苏传 我不装了 4/7
前端·javascript·vue.js
吴声子夜歌4 小时前
ES6——二进制数组详解
前端·ecmascript·es6
码事漫谈4 小时前
手把手带你部署本地模型,让你Token自由(小白专属)
前端·后端
ZC跨境爬虫4 小时前
【爬虫实战对比】Requests vs Scrapy 笔趣阁小说爬虫,从单线程到高效并发的全方位升级
前端·爬虫·scrapy·html
爱上好庆祝4 小时前
svg图片
前端·css·学习·html·css3
王夏奇5 小时前
python中的__all__ 具体用法
java·前端·python
大家的林语冰5 小时前
《前端周刊》尤大开源 Vite+ 全家桶,前端工业革命启动;尤大爆料 Void 云服务新产品,Vite 进军全栈开发;ECMA 源码映射规范......
前端·javascript·vue.js
jiayong236 小时前
第 8 课:开始引入组合式函数
前端·javascript·学习