当前位置: 首页 > article >正文

从vue底层原理上解释ref和reactive的区别

在 Vue 3 中,ref 和 reactive 是用于创建响应式数据的两种核心 API,它们的底层实现和适用场景有显著差异。以下是它们的区别及底层原理的详细分析:

1. ref

ref 主要用于包装基本数据类型(如 number、string、boolean),使其变为响应式。ref 会将原始值包装成一个带有 value 属性的对象,拦截 value的读取操作。

但如果 ref 接收的是引用数据类型(对象或数组),内部会调用 reactive 将其转换为响应式对象。vue3中基于基于 Proxy 实现,得到的是一个Proxy 代理对象
在这里插入图片描述
当我们看看调用了一个ref()时,做了哪些事情?

// 调用ref()时,内部会返回调用createRef方法后得到的结果
export function ref(value?: unknown) {
  return createRef(value, false)// 第二个参数:是否为shallowRef,因为shallowRef也复用了这个方法
}
function createRef(rawValue: unknown, shallow: boolean) {
// 如果传入的值已经是一个ref,则直接返回
  if (isRef(rawValue)) {
    return rawValue
  }
  //否则创建一个新的ref实例
  return new RefImpl(rawValue, shallow)
}

在这里插入图片描述

//ref实现类
class RefImpl<T = any> {
  _value: T // 响应式值
  private _rawValue: T // 原始值

  dep: Dep = new Dep()// 依赖追踪

  public readonly [ReactiveFlags.IS_REF] = true
  public readonly [ReactiveFlags.IS_SHALLOW]: boolean = false

  constructor(value: T, isShallow: boolean) {
  // 如果是shallowRef(isShallow为true),返回传入的值,否则将传入值转为原始值
    this._rawValue = isShallow ? value : toRaw(value)
  // 如果是正常的ref,将该值转为响应式值
    this._value = isShallow ? value : toReactive(value)
    this[ReactiveFlags.IS_SHALLOW] = isShallow
  }
// 访问value属性时会触发getter
  get value() {
  // 追踪依赖
    if (__DEV__) {
      this.dep.track({
        target: this,
        type: TrackOpTypes.GET,
        key: 'value',
      })
    } else {
      this.dep.track()
    }
    // 返回私有属性_value的值
    return this._value
  }
// 更改value属性时会触发setter
  set value(newValue) {
    const oldValue = this._rawValue
    const useDirectValue =
      this[ReactiveFlags.IS_SHALLOW] ||
      isShallow(newValue) ||
      isReadonly(newValue)
    newValue = useDirectValue ? newValue : toRaw(newValue)
    if (hasChanged(newValue, oldValue)) {
      this._rawValue = newValue
      this._value = useDirectValue ? newValue : toReactive(newValue)
      if (__DEV__) {
      //开发模式中,这些详细信息可以帮助更好的调试
        this.dep.trigger({
          target: this,
          type: TriggerOpTypes.SET,
          key: 'value',
          newValue,
          oldValue,
        })
      } else {
      // 生产模式中这些详细信息被忽略,为了减少开销和改善性能
        this.dep.trigger()
      }
    }
  }
}
export const toReactive = <T extends unknown>(value: T): T =>
  isObject(value) ? reactive(value) : value //  如果是引用数据类型,内部会调用 reactive将其转换为响应式对象

2. reactive

reactive 用于将对象或数组转换为响应式对象,基于 Proxy 实现。

// target:需要转换为响应式对象的普通对象
export function reactive(target: object) {
  // 如果目标对象是只读proxy,则直接返回该对象
  if (isReadonly(target)) {
    return target
  }
  // 创建响应式对象
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap,
  )
}
export const readonlyMap: WeakMap<Target, any> = new WeakMap<Target, any>()
function createReactiveObject(
  target: Target,//需要代理的目标对象
  isReadonly: boolean,//目标对象是否只读
  baseHandlers: ProxyHandler<any>,//普通对象的代理处理器(Proxy 的 get 和 set 等拦截器
  collectionHandlers: ProxyHandler<any>,//集合类型(如 Map、Set)的代理处理器
  proxyMap: WeakMap<Target, any>,//全局的响应式对象缓存,用于存储已经代理过的对象,避免重复代理。
) {
// 检查目标是否是对象类型,如果不是则直接返回
  if (!isObject(target)) {
 // 在开发环境下,会发出警告,提示无法将非对象类型转换为响应式或只读代理
    if (__DEV__) {
      warn(
        `value cannot be made ${isReadonly ? 'readonly' : 'reactive'}: ${String(
          target,
        )}`,
      )
    }
    return target
  }
  // 检查目标是否已经是代理对象,如果是则直接返回
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // 检查目标类型是否有效,如果无效则直接返回
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  //如果目标对象已经存在于 proxyMap 中,直接返回缓存的代理对象
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  //创建代理对象
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
  )
  // 将代理对象添加到 proxyMap 缓存中
  proxyMap.set(target, proxy)
  return proxy
}

🔗reactive这部分全部源代码

3. 使用场景

(1)ref 适用场景

  • 基本类型数据(如 number、string)。

  • 需要明确区分响应式变量和非响应式变量时。

  • 需要将值传递到外部函数或组件时(通过 .value 明确操作)。

(2)reactive 适用场景

  • 复杂对象或数组。

  • 需要直接操作属性(无需 .value)。

  • 需要保持对象结构的完整性时。


http://www.kler.cn/a/560158.html

相关文章:

  • 【NLP 24、实践 ⑤ 计算Bert模型中的参数数量】
  • 2024年国赛高教杯数学建模D题反潜航空深弹命中概率问题解题全过程文档及程序
  • 网络安全产品
  • 数据安全_笔记系列02:国密算法(商用密码算法)详解
  • 测试工程师玩转DeepSeek之Prompt
  • 为什么java从json中获取值有数据类型,而从xml中获取值没有数据类型?
  • vue3学习4-pinia+组件通信
  • python与C系列语言的差异总结(2)
  • Apache Doris:一款高性能的实时数据仓库
  • 分班问题幼儿园分班
  • 基于 SpringBoot 的 “电影交流平台小程序” 系统的设计与实现
  • 学习笔记04——JMM内存模型
  • zookeeper的可视化界面
  • 搜广推校招面经二十八
  • 【Docker】如何在Linux、Windows、MacOS中安装Docker
  • 包装类缓存对象
  • Excel带日期画折线图教程
  • 【量化策略】动量反转策略
  • ubuntu 20.04系统离线安装nfs
  • 【JavaScript】什么是JavaScript?以及常见的概念