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 更省事但更容易过度依赖
相关推荐
~无忧花开~2 小时前
React元素渲染:核心概念全解析
开发语言·前端·javascript·react.js
harrain2 小时前
antv x6graph使用经验
前端·antv·x6
开开心心就好2 小时前
免费无广告的礼金记账本,安卓应用
java·前端·ubuntu·edge·pdf·负载均衡·语音识别
marsh02062 小时前
14 openclaw模板引擎使用:高效渲染动态内容
java·前端·spring·ai·编程·技术
Mr数据杨2 小时前
【通用Vue】学生管理模块通用功能
javascript·vue.js·ecmascript
前端小菜鸟也有人起2 小时前
vue中is的作用和用法
前端·javascript·vue.js
m0_502724952 小时前
vue3在线预览excel表格
javascript·vue.js·excel
Never_Satisfied2 小时前
将web服务绑定在 1024 以下的端口上
前端·编辑器·vim
酉鬼女又兒2 小时前
零基础入门前端JavaScript 基础语法详解(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·javascript·chrome·蓝桥杯