一、一句话终极总结(背这个)
Vue3 响应式 = Proxy 代理 + Reflect 操作 + get 收集依赖 + set 触发更新 + 副作用调度更新
它是一套自动追踪数据变化、自动更新视图的系统。
二、响应式核心四件套
- Proxy:劫持对象的读取、修改、删除
- Reflect:安全操作对象属性
- track:收集依赖(谁用了这个数据)
- trigger:触发更新(数据变了通知谁)
三、完整底层流程(最核心)
第一步:创建响应式对象
js
const state = reactive({ name: "zs" })
底层:
- 传入对象 → 创建 Proxy 代理
- 劫持
get / set / deleteProperty
第二步:读取数据 → 收集依赖 track()
js
<div>{{ state.name }}</div>
执行:
- 读取
state.name→ 触发 Proxy get - 调用
track()收集当前组件的渲染副作用 - 建立映射:
target -> key -> effect
第三步:修改数据 → 触发更新 trigger()
js
state.name = "ls"
执行:
- 修改
state.name→ 触发 Proxy set - 调用
trigger()找到收集的副作用 - 把副作用加入队列,等待执行
第四步:执行副作用 → 更新视图
- 异步队列执行渲染函数
- 页面更新
四、依赖收集的完整结构(源码级)
Vue 用一个三层 Map 存储依赖:
targetMap (WeakMap)
↓
key: 原对象
value: depsMap (Map)
↓
key: 属性名
value: dep (Set)
↓
存放 effect(组件更新函数)
一句话:
哪个对象 → 哪个属性 → 被哪些组件使用
五、ref 到底是什么?(深入版)
js
const count = ref(0)
底层是一个 RefImpl 类:
- 用
get value()收集依赖 - 用
set value()触发更新 - 如果传入对象 → 自动调用
reactive - 如果是基本类型 → 直接存取器监听
六、computed 底层(懒计算 + 缓存)
- 自带 lazy 懒执行
- 有缓存,依赖不变不会重新计算
- 本质是一个 特殊的 effect
七、watch 底层
- 本质是一个 scheduler 调度器
- 监听指定数据
- 变化时执行回调
- 支持 deep / immediate / flush
八、Vue3 响应式流程图
创建 reactive/ref
↓
页面渲染 → 读取属性 → get 拦截 → track 收集依赖
↓
修改属性 → set 拦截 → trigger 触发更新
↓
执行 effect → 重新渲染
九、面试终极 10 问(答案直接背)
1. Vue3 响应式原理?
Proxy 代理对象,get 收集依赖,set 触发更新,依赖存储在 targetMap,更新时执行副作用。
2. 为什么用 Proxy 不用 defineProperty?
- 支持新增/删除属性
- 支持数组全操作
- 惰性监听,性能更高
- 支持 13 种拦截
3. ref 和 reactive 区别?
- ref 支持所有类型,reactive 只支持对象
- ref 需要 .value
- ref 底层是 class,reactive 是 Proxy
4. 依赖收集收集了什么?
收集了组件的渲染副作用(更新函数)
5. 为什么要用 Reflect?
- 保证 this 正确
- 有返回值
- 配合 Proxy 标准操作
6. shallowRef/shallowReactive 原理?
不递归代理,只监听第一层,修改内部不触发更新。
7. reactive 赋值丢失响应式?
reactive 是 Proxy 对象,直接赋值变成普通对象。
8. toRef/toRefs 作用?
解构 reactive 时保持响应式。
9. computed 和 watch 区别?
- computed 有缓存,必须 return
- watch 无缓存,执行副作用
10. Vue3 响应式有什么缺点?
- 解构会丢失响应式
- 代理对象不等于原对象
- 某些第三方库实例不能代理
十、最终超级总结
Vue3 响应式 = Proxy + track + trigger + effect
- 读取 → 收集依赖
- 修改 → 触发更新
- ref 处理基本类型
- reactive 处理对象
- computed 缓存
- watch 监听