直接回答:Vue 3 用 Proxy 拦截对象读写:get 时 track 收集依赖(记录“哪个 effect 读了哪个对象的哪个 key”),set 时 trigger 派发更新(找出依赖该 key 的 effect 重新执行)。依赖存储是三层 WeakMap:target → Map(key → Set(effect)),读取即建立“数据—副作用”的订阅关系,写入即通知,实现精确到属性级的自动更新。
展开解析:细节展开:effect 是响应式副作用的抽象(组件渲染函数、computed、watch 都是 effect),执行前把自己压入 activeEffect 栈,执行中任何响应式读取都被 track 关联——嵌套 effect(组件嵌套渲染)靠栈结构正确归属。computed 的惰性:内部 effect 标记 lazy 不立即跑,依赖变化只置 dirty,下次取值才重算,且结果对下游也是响应式(computed 自己有依赖收集)。trigger 的分级调度:effect 可声明调度器——组件渲染 effect 进异步队列(nextTick 微任务批量合并,同 tick 多次写只渲染一次),watch 默认 post 时机,watchEffect 前置。Proxy 相对 defineProperty 的收益:可拦截新增/删除属性、数组索引与 length、in/has 等操作,无需 Vue 2 的 $set 补丁与数组方法劫持;代价是 Proxy 不可 polyfill、原始值必须包 ref(.value 的 get/set 走 class 拦截)。边界情况:数组 length 依赖、Map/Set 的专属拦截(collectionHandlers)、readonly 的只 track 不 trigger、shallowRef 的性能场景。调试利器:effect 的 onTrack/onTrigger 钩子打印依赖链路。
追问方向:为什么组件更新默认异步队列?computed 缓存失效的传播链是怎样的?
(约 490 字)