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
  • 全局配置:应用级的不可变配置对象
  • 开发辅助:在开发模式下防止意外修改共享状态

这种实现确保了只读对象的完整保护,同时保持了响应式系统的性能优化和类型安全特性。

Vue3 的响应式系统源码级 readonly 的实现原理与深层只读处理机制 readonly 是 Vue3 响应式系统的重要 API,它创建一个只读的响应式对象,任何修改尝试都会触发警告。下面详细解析其实现原理: 1. readonly 的基本定义与特性 功能:接收一个对象返回其只读代理,禁止任何修改操作 特性:深层只读(嵌套对象也会被处理)、响应式追踪(仍可收集依赖) 与 shallowReadonly 区别:shallowReadonly 只处理第一层属性 2. readonly 的创建流程 3. readonlyHandlers 的实现细节 5. 深层只读的递归处理机制 当访问嵌套对象属性时,getter 会检测属性值类型 如果属性值是对象或数组,会递归调用 readonly() 进行处理 这确保了整个对象树的只读特性: 6. 集合类型的特殊处理 对于 Map、Set 等集合类型,使用 readonlyCollectionHandlers: 7. 实现的关键技术点 缓存机制 :使用 WeakMap 缓存代理对象,避免重复代理 类型标记 :通过 ReactiveFlags.IS_ READONLY 标识只读对象 深层代理 :在 getter 中懒递归,避免一次性深度遍历的性能开销 依赖追踪 :虽然不能修改,但仍需要追踪访问以维持响应式一致性 8. 实际应用场景 Props 传递:组件接收的 props 会被自动转为 readonly 全局配置:应用级的不可变配置对象 开发辅助:在开发模式下防止意外修改共享状态 这种实现确保了只读对象的完整保护,同时保持了响应式系统的性能优化和类型安全特性。