Vue3 的响应式系统源码级 shallowReactive 与 shallowReadonly 的实现原理与区别
字数 1288 2025-11-27 08:24:19
Vue3 的响应式系统源码级 shallowReactive 与 shallowReadonly 的实现原理与区别
1. 知识描述
shallowReactive 和 shallowReadonly 是 Vue3 响应式系统中用于控制响应式深度的 API。它们的核心区别在于:
shallowReactive只对对象的第一层属性进行响应式处理,深层属性保持原始状态。shallowReadonly只对对象的第一层属性进行只读代理,深层属性可被修改但不会触发响应。
理解其实现需结合 Vue3 的 Proxy 代理机制和递归响应式转换逻辑。
2. 实现原理分析
(1)shallowReactive 的源码级实现
- 创建代理:与
reactive类似,使用Proxy拦截对象的读写操作,但关键区别在于get拦截器不会递归调用reactive。 - get 拦截器逻辑:
const shallowGet = (target, key, receiver) => { // 依赖收集(与 reactive 相同) track(target, key); const res = Reflect.get(target, key, receiver); // 关键:直接返回原始值,不进行深层响应式转换 return res; }; - set 拦截器逻辑:
const shallowSet = (target, key, value, receiver) => { const oldValue = target[key]; // 触发更新(仅针对第一层属性) const result = Reflect.set(target, key, value, receiver); if (hasChanged(value, oldValue)) { trigger(target, key); } return result; }; - 深层属性处理:若访问
obj.nested(嵌套对象),返回的是原始对象而非响应式代理,修改obj.nested.x不会触发响应。
(2)shallowReadonly 的源码级实现
- 只读代理的拦截逻辑:
const shallowReadonlyHandlers = { get: (target, key, receiver) => { // 第一层属性只读,但不收集依赖(因为不会触发更新) const res = Reflect.get(target, key, receiver); return res; }, set: (target, key, value) => { // 第一层属性禁止修改(开发环境会报警告) console.warn(`Set operation on key "${key}" failed: target is readonly.`); return true; } }; - 深层属性特性:若修改
obj.nested.x,由于obj.nested是原始对象,修改不会触发警告或响应,但直接修改obj.nested(如obj.nested = {})会被set拦截器阻止。
3. 与 reactive/readonly 的对比
| API | 第一层属性 | 深层属性 |
|---|---|---|
reactive |
响应式 | 递归响应式 |
shallowReactive |
响应式 | 原始对象(无响应) |
readonly |
只读 | 递归只读 |
shallowReadonly |
只读 | 原始对象(可修改但无响应) |
4. 设计动机与使用场景
- 性能优化:避免深层递归代理大型对象(如配置信息)。
- 可控响应式粒度:明确指定哪些层级需要响应式(如组件 props 的浅层只读特性)。
- 与
markRaw协同:若深层属性需完全避免响应式,可结合markRaw标记。
5. 总结
shallowReactive 和 shallowReadonly 通过简化 Proxy 拦截器的递归逻辑,实现了响应式粒度的精细控制。其核心区别在于深层属性的处理策略,前者保持原始值可修改但无响应,后者则完全禁止第一层修改而放任深层操作。