【Vue3源码学习】— CH2.1 reactive.ts详解

1. APIs

在reactive.ts里,主要提供了四个API和一些其他方法

API功能使用场景
reactive将普通对象转换为响应式对象。这个转换是深层的,即对象内部所有嵌套的属性也都会变成响应式的。当你需要一个深层响应式的对象,以便在对象或其任何嵌套属性改变时更新UI。常用于存储和管理组件的状态。
shallowReactive将普通对象转换为响应式对象,但转换仅限于对象的第一层属性。当你只需要对象的顶层属性是响应式的,而嵌套对象不需要转换为响应式。这可以在处理大型对象或深层嵌套对象时减少性能开销。
readonly将普通对象转换为只读的响应式对象。这个转换是深层的,即对象内部所有嵌套的属性也都会变成只读的。用于创建一个不应该被修改的响应式对象,常用于将状态传递给子组件时,确保子组件不能修改这些状态。
shallowReadonly将普通对象转换为只读的响应式对象,但转换仅限于对象的第一层属性。当你想要保护对象的顶层属性不被修改,同时不需要对象内部嵌套的属性是只读的。适用于优化性能,特别是当嵌套属性很大时。

本文以reactive API来说明在vue底层是如何实现的

2. 使用示例

在页面上显示count值,【+1】按钮修改count值
在这里插入图片描述

3. 调用reactive时,发生了什么?

查看源码 @vue/reactivity/reactive.ts,首先解读注释内容:

3.1 返回一个对象的响应式代理:

使用reactive函数时,它会返回原始对象的一个代理(Proxy)。这个代理对象能够拦截对原始对象的各种操作(如属性读取、写入等),使Vue能够追踪这些操作,并在必要时自动更新UI。

3.2 响应式转换是“深度”的:

当你对一个对象使用reactive时,不仅该对象本身变为响应式的,它的所有嵌套属性也都将递归地被转换为响应式的。这意味着,无论对象结构有多深,Vue都能追踪到任何属性的变化,并在属性值改变

3.3 深度展开ref属性并保持响应性:

如果对象的某个属性是一个ref(引用),在通过reactive转换的响应式对象中访问该属性时,你将直接得到ref内部的值,而不需要使用.value来访问。这个过程称为“深度展开”或“自动解引用”。重要的是,尽管ref被自动解引用,但对这些值的读取和修改仍然是响应式的,Vue会追踪这些操作并在值变化时更新UI。

源代码解读

/**
 * 返回一个对象的响应式代理
 * 响应式转换是“深度”的:它的所有嵌套属性也都将递归地被转换为响应式的
 * 深度展开ref属性并保持响应性:如果对象的某个属性是一个ref(引用),
 * 在通过reactive转换的响应式对象中访问该属性时,你将直接得到ref内部的值,
 * 而不需要使用.value来访问
 */
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
  //如果尝试将一个已经是只读代理(readonly proxy)的对象转换为响应式对象,则直接返回这个只读版本的对象。
  if (isReadonly(target)) {
    return target
  }

  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap,
  )
}

4. 创建Proxy: createReactiveObject()

这个函数是创建响应式代理对象(包括响应式、只读、浅响应式、浅只读)的底层逻辑。它根据传入的参数决定代理对象的行为

function createReactiveObject(
  target: Target,  //要被代理的原始对象
  isReadonly: boolean,  //创建的代理将是否是只读的
  baseHandlers: ProxyHandler<any>,  //用于普通对象的Proxy处理器
  collectionHandlers: ProxyHandler<any>, //用于集合类型对象的Proxy处理器
  proxyMap: WeakMap<Target, any>, //用于存储原始对象与其代理之间的映射
) {
  // 确保target是对象
  if (!isObject(target)) {
    if (__DEV__) {
      warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // 如果target已经是代理对象(通过target上的[ReactiveFlags.RAW]属性来判断),直接返回:
  // 例外: 对于在reactive对象上的readonly调用是个例外
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // 检查是否已存在代理
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // 检查target类型是否有效(只有特定的对象类型可以被观察)
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  // 创建代理
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
  )
  proxyMap.set(target, proxy)
  return proxy
}

5. 小结

总之,reactive.ts的核心功能就是返回一个proxy,而这个proxy具体是如何实现响应式,请看后续章节。
在这里插入图片描述

  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值