Vue3 的响应式系统源码级 readonly 的实现原理与深层只读处理机制
字数 711 2025-11-30 16:46:48
Vue3 的响应式系统源码级 readonly 的实现原理与深层只读处理机制
readonly 是 Vue3 响应式系统的重要 API,它创建一个只读的响应式对象,任何修改尝试都会触发警告。下面详细解析其实现原理:
1. readonly 的基本定义与特性
- 功能:接收一个对象返回其只读代理,禁止任何修改操作
- 特性:深层只读(嵌套对象也会被处理)、响应式追踪(仍可收集依赖)
- 与 shallowReadonly 区别:shallowReadonly 只处理第一层属性
2. readonly 的创建流程
// 1. 入口函数
function readonly(target) {
return createReactiveObject(
target,
true, // isReadonly 标记
readonlyHandlers,
readonlyCollectionHandlers
)
}
// 2. 创建响应式对象的核心函数
function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers) {
// 检查缓存,避免重复代理
const proxyMap = isReadonly ? readonlyMap : reactiveMap
if (proxyMap.get(target)) {
return proxyMap.get(target)
}
// 使用 Proxy 创建代理
const proxy = new Proxy(
target,
collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
3. readonlyHandlers 的实现细节
const readonlyHandlers = {
get: createGetter(true), // isReadonly = true
set(target, key) {
// 在开发模式下输出警告
if (__DEV__) {
console.warn(`Set operation on key "${key}" failed: target is readonly.`)
}
return true
},
deleteProperty(target, key) {
// 同样禁止删除操作
if (__DEV__) {
console.warn(`Delete operation on key "${key}" failed: target is readonly.`)
}
return true
}
}
// 4. 创建 getter 拦截函数
function createGetter(isReadonly = false) {
return function get(target, key, receiver) {
// 对内置符号的特殊处理
if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
}
const res = Reflect.get(target, key, receiver)
// 深层只读处理:如果值是对象,递归创建 readonly
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
// 依赖收集(即使是 readonly 也要追踪访问)
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
return res
}
}
5. 深层只读的递归处理机制
- 当访问嵌套对象属性时,getter 会检测属性值类型
- 如果属性值是对象或数组,会递归调用 readonly() 进行处理
- 这确保了整个对象树的只读特性:
const original = { a: 1, nested: { b: 2 } }
const readOnlyObj = readonly(original)
// 尝试修改嵌套属性也会被阻止
readOnlyObj.nested.b = 3 // 触发警告
6. 集合类型的特殊处理
对于 Map、Set 等集合类型,使用 readonlyCollectionHandlers:
const readonlyCollectionHandlers = {
get: createGetter(true)
// 所有修改方法(set、add、delete等)都被重写为警告函数
}
7. 实现的关键技术点
- 缓存机制:使用 WeakMap 缓存代理对象,避免重复代理
- 类型标记:通过 ReactiveFlags.IS_READONLY 标识只读对象
- 深层代理:在 getter 中懒递归,避免一次性深度遍历的性能开销
- 依赖追踪:虽然不能修改,但仍需要追踪访问以维持响应式一致性
8. 实际应用场景
- Props 传递:组件接收的 props 会被自动转为 readonly
- 全局配置:应用级的不可变配置对象
- 开发辅助:在开发模式下防止意外修改共享状态
这种实现确保了只读对象的完整保护,同时保持了响应式系统的性能优化和类型安全特性。