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

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

何为reactive

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

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

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

html 复制代码
<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创建了一个响应式对象state,并且给按钮添加点击事件来修改响应式对象state中count的值,在页面不刷新的情况下我们看见每当点击都会使数字+1。

手写一个reactive

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

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

js 复制代码
//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;
    // 更新浏览器的视图(响应式)
  }
}
js 复制代码
// 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),这三个参数分别代表代理前对象已经被代理过的对象和该对象被操作修改时会触发的函数对象。

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

js 复制代码
const state = reactive({
  count: 1,
})

const state2 = reactive({
  state
})

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

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

html 复制代码
<!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剖析加手写对君有所帮助,创作不易可否给个小赞赞支持一下!

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

相关推荐
Cloud_bread21 分钟前
从ReAct到自主闭环:Agentic Coding核心执行引擎的技术演进
前端·react.js·前端框架
cidy_9830 分钟前
OptMem 使用教程
前端
杉氧34 分钟前
用 Compose 挑战交互与动效天花板:ComposeCraftLab 开源实验室全解析
android·前端·kotlin
Canace43 分钟前
AI 生成到 90% 突然断了:你的解决方案是?
前端·人工智能
TinssonTai1 小时前
Vite 8 版 Chrome 插件全家桶,popup/options/sidepanel 一次集齐
前端·vue.js
玉鸯1 小时前
让 Agent 面向用户:AG-UI 协议构建 Agent 前端
前端·python·agent
程序员黑豆1 小时前
鸿蒙应用开发 @Extend 装饰器使用教程
前端·harmonyos
杨先生哦1 小时前
【2026热端攻防系列 10/12】前端凭据安全深度攻防:Cookie/Storage劫持、会话固定、凭据泄露与浏览器最新加固方案
前端·笔记·安全·web安全
莫石2 小时前
坦克打无人机模拟(three-tile 地形)
前端
hunterandroid2 小时前
[鸿蒙从零到一] HarmonyOS 任务调度与并发模型实战:taskpool、Worker 与可取消任务
前端