Vue 3的reactive手写实现

本文详细介绍了如何手动实现Vue 3的reactive特性,包括基础版reactive的创建,解决this指向问题,处理代理对象被再次代理以及对象被代理多次的情况。通过使用Reflect API和WeakMap,确保了代理行为的正确性和一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

本文将介绍如何手写实现Vue 3的reactive,涉及到代理对象、代理对象被再次代理和一个对象被代理多次等问题的处理方法。如果您正在学习Vue 3或者对于Vue 3的reactive实现感兴趣,本文可以帮助您更好地理解其原理和实现。

基础版reactive

首先,我们需要定义一个isObject的公共方法,用于判断一个值是否为对象:

export const isObject = (value) => {
  return typeof value === 'object' && value !== null
}

然后,我们使用ES6中的proxy API来创建一个简单的代理对象,实现对象的基本操作:

export function reactive(target) {
  // 需要首先判断是否是对象,不是直接返回
  if (!isObject(target)) return

  const proxy = new Proxy(target, {
    get(target, key, receiver) {
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver)
      return result
    }
  })

  return proxy
}

在上述代码中,我们首先判断了传入的参数是否为对象,然后创建一个proxy对象,并在get和set方法中使用Reflect API对原对象进行代理。

使用示例:

<script src="../../../../node_modules/@vue/reactivity/dist/reactivity.global.js"></script>
<script>
  const { reactive } = VueReactivity
  const obj = {
    name: 'dy',
    age: 25,
    get fn() { return this.age }
  }
  const state = reactive(obj)
</script>

注意:代码中使用了Vue 3中的reactivity依赖。

this指向问题的解决

在我们进行对象代理的时候,我们可能会遇到如下问题:

当我们在代理对象中的函数中使用this关键字时,this并不指向代理对象,而是指向原对象。为了解决这个问题,我们需要使用Reflect API来监听代理对象的属性变化。

修改后的代码如下:

export function reactive(target) {
  if (!isObject(target)) return
  if (target[ReactiveFlag.IS_REACTIVE]) return target

  const proxy = new Proxy(target, {
    get(target, key, receiver) {
      if (key === ReactiveFlag.IS_REACTIVE) return true
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver)
      return result
    }
  })

  return proxy
}

在get方法中,我们使用了if (key === ReactiveFlag.IS_REACTIVE)来判断取值的key是否为_v_isReactive,如果是则返回true;在set方法中,我们同样使用了Reflect API来设置值,并将结果返回。

代理对象被再次代理

在Vue 3中,代理对象被再次代理时,应该是相同的。但是,如果我们只是使用之前的代码来实现,代理对象被再次代理时输出的结果将会是false。那么我们该怎么解决这个问题呢?

我们可以使用WeakMap来将原对象和代理对象进行关联,如果对象已经被代理过,直接返回该代理对象即可。

代码如下:

const reactiveMap = new WeakMap()

export function reactive(target) {
  if (!isObject(target)) return
  if (target[ReactiveFlag.IS_REACTIVE]) return target

  const exisitingProxy = reactiveMap.get(target)
  if (exisitingProxy) return exisitingProxy

  const proxy = new Proxy(target, {
    get(target, key, receiver) {
      if (key === ReactiveFlag.IS_REACTIVE) return true
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver)
      return result
    }
  })

  reactiveMap.set(target, proxy)

  return proxy
}

在上述代码中,我们定义了一个reactiveMap来存储原对象和代理对象的关联关系。在函数执行时,我们首先判断对象是否被代理过,如果已经被代理过,则直接返回该代理对象;如果未被代理过,则创建一个新的代理对象,并将其与原对象关联起来。

一个对象被代理多次

当一个对象被代理多次时,我们应该返回同一个代理对象,而不是多个代理对象。为了解决这个问题,我们可以在reactive函数中使用WeakMap来记录对象和代理对象之间的映射关系。

代码如下:

const reactiveMap = new WeakMap()

export function reactive(target) {
  if (!isObject(target)) return
  if (target[ReactiveFlag.IS_REACTIVE]) return target

  const exisitingProxy = reactiveMap.get(target)
  if (exisitingProxy) return exisitingProxy

  const proxy = new Proxy(target, {
    get(target, key, receiver) {
      if (key === ReactiveFlag.IS_REACTIVE) return true
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver)
      return result
    }
  })

  reactiveMap.set(target, proxy)

  return proxy
}

在上述代码中,我们使用了reactiveMap来记录对象和代理对象之间的映射关系。如果对象已经被代理过,则直接返回之前的代理对象;如果未被代理过,则创建一个新的代理对象,并将其与原对象关联起来。

结论

通过上述代码的实现,我们可以得出如下结论:代理对象被再次代理时,应该相同;个对象被代理多次时,应该相同。

结语

在本文中,我们介绍了如何手写实现Vue 3的reactive,并解决了代理对象被再次代理和一个对象被代理多次等问题。

感兴趣的朋友可以点击关注作者哦(●'◡'●)!。 如果不足,请多指教。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开心上班

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值