958 字
约 3 分钟
6
S14-B03 · 响应式:ref / reactive / 计算 / 侦听

适用读者:被"watch 没触发"坑过的人。
前置:S14-B01。
实测环境:Vue 3.5 / Node 22.20(无 DOM,纯响应式系统,2026-11-05)。
覆盖旧文:1489(44) 1490(50) 1491(25) = 119 点。
覆盖扩展:E-S14-003(Proxy 依赖收集)、E-S14-004(watch 异步批处理)、E-S14-006(事件循环)。
上一篇:S14-B02 · 模板语法:插值、绑定、事件、条件、循环
下一篇:S14-B04 · 样式方案。


1. 一句话结论

Vue 3 用 Proxy 拦截读写:读的时候收集依赖、写的时候通知更新。effect 同步执行,watch 默认异步批处理——这是"我改了值但回调没跑"的根因。


2. 实测:effect 同步、watch 异步

const n = ref(1)
watch(n, (nv, ov) => log.push(`watch: ${ov} → ${nv}`))
effect(() => log.push(`effect: n=${n.value}`))
n.value = 2
console.log(log)              // 同步打印

实测输出:

同步打印时 log = ["effect: n=1","effect: n=2"]
微任务后 log = ["effect: n=1","effect: n=2","watch: 1 → 2"]
观察 结论
同步打印时只有 effect effect 同步跑
watch 在微任务后出现 watch 异步(默认 pre flush)

3. 实测:连续改值,watch 只触发一次

n.value = 3; n.value = 4
await Promise.resolve()

实测:

连续两次修改后 log = [...,"effect: n=3","effect: n=4","watch: 2 → 4"]
观察 结论
effect 跑了 两次(3、4) effect 是同步的
watch 只出现 一次(2 → 4) 批处理:只保留最后一次

"这是 bug 吗":不是——watch 的设计目标就是"状态稳定后再响应"。要每次变更都触发 → flush: 'sync'。


4. ref / reactive / computed 的边界

API 适用 注意
ref(x) 任意值(含基本类型) 模板里自动解包,JS 里要 .value
reactive(obj) 对象/数组 解构会丢失响应性
computed(fn) 派生值 有缓存,依赖不变不重算
watch(src, cb) 副作用 异步批处理
watchEffect(fn) 自动收集依赖 同步执行

"为什么 reactive 解构会失效":解构出来的只是普通值,丢失了 Proxy 包装。


5. 事件循环:理解异步的地基(E-S14-006)

实测(Node 22,CJS):

1 sync 开始
2 sync 结束
3 nextTick
4 promise.then
5 queueMicrotask
6 setTimeout(0)
7 setImmediate

实测(Node 22,ESM):

1 sync 开始
2 sync 结束
4 promise.then
5 queueMicrotask
3 nextTick      ← 与 CJS 顺序不同
6 setTimeout(0)
7 setImmediate

结论:微任务(Promise / queueMicrotask)先于宏任务(setTimeout / setImmediate);而 nextTick 与 Promise 的相对顺序在 CJS/ESM 下不同(实测复现两次)——业务代码不要依赖这两者的相对顺序。

watch 的"微任务后"就落在这一层:改值 → 微任务刷新队列 → 回调执行。


6. 五个误区

误区 真相
"watch 改完就同步触发" 默认异步批处理(实测)
"computed 每次都重算" 有缓存,依赖不变不重算
"reactive 解构仍响应" 丢响应性
"ref 在 JS 里不用 .value" 需要(模板里才自动解包)
"nextTick 与 Promise 顺序固定" CJS/ESM 不同(实测)

7. 自检

  1. effect 与 watch 的执行时机差异?
  2. 连续两次改值,为什么 watch 只触发一次?怎么让它每次都触发?
  3. ref 与 reactive 的适用差异?
  4. 微任务与宏任务谁先?
  5. 为什么不该依赖 nextTick 与 Promise 的顺序?

8. 下一篇

S14-B04 · 样式方案:Tailwind 与 Animate.css

响应式讲完,下一篇讲样式:原子化 CSS 到底解决了什么。


本篇属于系列《S14 · 前端与可视化》。旧文来源:A-1489/1490/1491;扩展点 E-S14-003/004/006。effect/watch 时序与事件循环为 Vue 3.5 + Node 22 实测。

S14-B03 · 响应式:ref / reactive / 计算 / 侦听
https://www.clxhxhhr.top/posts/5374/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。