最近手有点痒,手搓一个reactive

在vue中,相信大家在使用响应式数据的时候经常碰面的两位伙伴就是refreactive了,今天我们就来聊聊用于将对象变成响应式的reactive,不去纠结到底是用ref还是reactive,我们先来手搓reactive再来拿捏ref源码,主打一个雨露均沾!

何为reactive

在 Vue 3 中,reactive 是 Vue 提供的一个函数,用于创建响应式的对象。这是 Vue 3 中 Composition API 的一部分,旨在提供更灵活且可组合的方式来组织和重用组件逻辑。

使用 reactive 函数,我们可以将普通 JavaScript 对象转化为响应式对象,这样当对象的属性发生变化时,相关的视图将会自动更新。

让我们通过以下一个简单的例子,看看在 Vue 3 中如何使用 reactive

<template>
  <div>
    <p>{{ state.count }}</p>
    <button @click="() => state.count++">add</button>
  </div>
</template>

<script setup>
import { reactive } from 'vue';

const state = reactive({
  count: 1,
})
</script>

最近手有点痒,手搓一个reactive

在上述代码中,我们通过reactive创建了一个响应式对象state,并且给按钮添加点击事件来修改响应式对象state中count的值,在页面不刷新的情况下我们看见每当点击都会使数字+1。

手写一个reactive

要搞定reactive就得首先有一个对象,然后有方法能够操作里面的数据,并且最后需要将该对象处理成响应式的。

那么,我们如何设置当响应式对象被处理数据值时触发对应的函数呢?我们来到ES6官方文档中第15条的Proxy解析可以看到,简单来讲他就是一个agent,它接收两个参数,第一个是被代理对象,第二个参数用于当被代理对象被读取值,设置值,判断值等等操作时会触发的函数

最近手有点痒,手搓一个reactive

//baseHandlers.JS
export const mutableHandlers = {
  get: function(target, key, receiver) { // target 被代理的原对象,key是原对象中的键,receiver代理后的对象
    console.log('target对象被读取值了');
    return target[key]
  },
  set: function(target, key, value, receiver) { // target
    console.log('target对象被修改值了', key, value);
    target[key] = value;
    // 更新浏览器的视图(响应式)
  }
}
// reactive.js
import { mutableHandlers } from './baseHandlers.js'

// 保存被代理过的对象
export const reactiveMap = new WeakMap() // new Map() // new WeakMap 对内存的回收更加友好


export function reactive(target) { // 将target变成响应式
  return createReactiveObject(target, reactiveMap, mutableHandlers)
}


export function createReactiveObject(target, proxyMap, proxyHandlers) { // 创建响应式的函数
  // 判断target是不是一个引用类型
  if (typeof target !== 'object' || target === null) {  // 不是对象就不给操作
    return target
  }

  // 该对象是否已经被代理过(已经是响应式对象)
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }

  // 执行代理操作(将target处理成响应式)
  const proxy = new Proxy(target, proxyHandlers) // 第二个参数的作用:当target被读取值,设置值,判断值等等操作时会触发的函数

  // 往 proxyMap 增加 proxy, 把已经代理过的对象缓存起来
  proxyMap.set(target, proxy)
  
  return proxy
}

首先,我们简化设置了Proxy中触发函数为修改值和设置值两种方法。

接着,我们就需要创建一个对象用来保存被代理过的对象,还有一个方法用于将对象变成响应式。

如何创建响应式对象?在上面我们已经知道了可以通过ES6中的Proxy来实现。于是我们可以自己创建一个函数createReactiveObject并且接收三个参数(target, proxyMap, proxyHandlers),这三个参数分别代表代理前对象已经被代理过的对象和该对象被操作修改时会触发的函数对象。

由于为了避免出现以下情况,我们还需要存储已经代理过的对象,用于避免二次或多次响应式化同一个对象而造成不必要的性能损耗。

const state = reactive({
  count: 1,
})

const state2 = reactive({
  state
})

在函数createReactiveObject中我们首先判断代理对象是否是引用类型,接着判断该引用类型是否已经被代理过,然后再执行代理操作, 也就是将该代理前的对象处理成响应式,最后再将该处理后的对象缓存起来,最后返回结果就可以啦!

那么最后,就让我们来在创建一个html文件检验一下手写的这份reactive是否有效呢?

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script type="module">
    import { reactive } from './reactive.js';

    const state = reactive({
      name: 'sAnL1ng',
      age: 18
    })

    console.log(state.name); // 'sAnL1ng'
    console.log(state.age); // 18
    
    setInterval(() => {
      state.age++
    }, 3000)
  </script>
</body>
</html>

需要注意打开页面需要用到Open with Live Server插件打开才能正常显示效果

最近手有点痒,手搓一个reactive

结语

希望这篇reactive剖析加手写对君有所帮助,创作不易可否给个小赞赞支持一下!

假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!

原文链接:https://juejin.cn/post/7337208702199726130 作者:sAnL1ng

(0)
上一篇 2024年2月20日 上午10:00
下一篇 2024年2月20日 上午10:10

相关推荐

发表回复

登录后才能评论