第 18 题:Vue3 响应式原理中的 effect、依赖收集与依赖触发
🎯 一、核心问题
问:Vue3 响应式系统中 effect 是什么?依赖是如何收集和触发的?
-
面试官希望你理解:
- 响应式依赖收集机制
- effect 栈的作用
- 数据变化如何触发对应组件重新渲染
🎯 二、核心概念
-
effect
- 本质是一个函数,用于包裹 副作用(如模板渲染函数、computed getter)
- 当依赖的数据发生变化时,effect 会被重新执行
-
依赖收集(track)
- 当 effect 执行过程中访问响应式对象属性时
- Proxy 的
get会调用track - 将当前激活的 effect 保存到该属性的依赖集合中
-
依赖触发(trigger)
- 当属性变化时,通过 Proxy 的
set调用trigger - 触发依赖集合中的所有 effect 执行
- 更新模板 / computed / watch 回调
- 当属性变化时,通过 Proxy 的
-
effect 栈
- 用来处理嵌套 effect
- 确保 track 时收集的始终是 当前激活的 effect
- 避免不同 effect 混淆依赖
🎯 三、深度图解(文字版图)
scss
effectStack = []
1. 创建 effect:
effect(() => { console.log(state.count) })
2. 执行 effect:
└─ push effect 到 effectStack
│
▼
Proxy get 拦截 state.count
│
▼
track(effectStack.top) → 保存依赖
3. 数据变化:
state.count++
└─ Proxy set 拦截
│
▼
trigger(state.count.deps) → 执行依赖的 effect
│
▼
effectStack.top 被重新执行 → DOM / computed 更新
关系总结:
scss
响应式对象 property → Set(effect1, effect2)
effect1 / effect2 → function(callback)
依赖变化 → trigger → 执行 effect
🎯 四、示例代码(最简单的实现效果)
javascript
import { reactive, effect } from 'vue'
const state = reactive({ count: 0 })
effect(() => {
console.log('count =', state.count)
})
state.count++ // 自动触发 effect
state.count++ // 再次触发 effect
分析:
effect执行 → push 到 effectStackstate.count的get调用 track → 收集 effectstate.count++的set调用 trigger → 执行 effect → 输出最新值
🎯 五、面试官常见追问
追问 1:effect 栈为什么必要?
- 支持嵌套 effect
- 确保 track 时收集的是当前激活 effect
- 避免 effect A 调用 effect B 时混乱收集依赖
追问 2:依赖收集如何避免重复?
- 每个属性依赖使用 Set 存储 effect
- 同一个 effect 多次访问同一属性只会收集一次
追问 3:computed 内部如何利用 effect?
- computed getter 包裹 effect
- effect 被标记
lazy = true - 访问
.value时触发执行并收集依赖 - 依赖变化 → 标记 dirty → 下次访问重新计算
追问 4:watch 是如何利用依赖触发的?
- watch 内部创建 getter effect
- 监听响应式对象 / getter
- 数据变化 → trigger → 执行回调
- 可获取新旧值,执行异步副作用
追问 5:为什么 Vue3 响应式性能比 Vue2 高?
- Proxy 不需要递归 defineProperty → 节省初始化时间
- 依赖收集精准 → 最小化 effect 执行
- effect + dirty + PatchFlag → 精准更新
🎯 六、一句话总结(面试必背)
Vue3 响应式通过 Proxy 拦截访问,effect 栈管理当前激活 effect,track 收集依赖,trigger 触发依赖执行,实现高性能、精准响应式更新。