trigger本质是通知已知依赖重新执行,它基于track阶段建立的路径级索引(各属性独立dep)精准派发更新任务,通过微任务队列调度effect避免重复执行,失效主因是track未发生。

Trigger 过程本质是“通知已知依赖重新执行”的动作,它不决定谁该更新,而是把更新任务派发给那些在 track 阶段已被记录下来的副作用函数(比如组件的 render effect、watchEffect 或 computed)。这个过程之所以能精准,是因为 Vue 在读取数据时就已建立了一对一(或一对多)的映射关系。
Trigger 的触发时机与入口
只要响应式数据发生可被拦截的变更,就会进入 trigger 流程。常见入口包括:
- Proxy 的 set 拦截器:如
state.count = 5 - 数组方法重写逻辑:如
arr.push(item)、arr.pop()、arr.splice() - Map/Set 原生方法调用(Vue 3.2+ 支持):如
map.set('key', val) - ref.value 赋值:内部会代理到对应的 trigger 调用
Trigger 如何定位要通知的依赖?
关键在于“路径级索引”——每个响应式属性都维护一个独立的依赖集合(Dep),而 Dep 是一个 Set,只存 effect 函数引用。
- 访问
state.user.name时,track 会分别收集到user对象和name属性两个层级的 Dep 中 - 修改
state.user.name时,trigger 先找到user的 Dep,再从中取出name对应的 Dep,最终遍历执行其中所有 effect - 数组操作还会额外触发
length和索引数字 key(如'0')的 trigger,确保 push/pop 等行为也能被监听
通知不是立即执行,而是调度进队列
为避免重复渲染和保证一致性,trigger 不会直接调用 effect,而是将其加入异步调度队列:
- 同一个 effect 在一次事件循环(tick)中只会入队一次,即使被多个 key 触发
- 使用 Promise.then 或 queueMicrotask 实现微任务调度,确保 DOM 更新前完成所有 effect 执行
- render effect 被调度后,会重新运行模板函数生成新 VNode,再与旧 VNode diff,仅更新真实 DOM 中变动的部分
什么情况下 Trigger 会失效?
Trigger 失效往往不是机制问题,而是 track 没发生,导致“没人被登记”。典型场景有:
- 解构赋值后使用:如
const { count } = state,后续用count++不触发任何 track/trigger - 直接索引赋值数组:如
arr[0] = 'new',Proxy 无法拦截,需改用arr.splice(0, 1, 'new')或ref包裹项 - 新增对象属性未通过
Vue.set或reactive初始化:普通对象添加属性不会自动 reactive - 模板中未使用该字段:没读取过,自然没被 track,也就不会出现在任何 Dep 里










