vue3源码(二)reactiveeffect

news2024/12/26 9:21:21

一.reactive与effect功能

reactive方法会将对象变成proxy对象, effect中使用reactive对象时会进行依赖收集,稍后属性变化时会重新执行effect函数。

<div id="app"></div>
    <script type="module">
      import {
     	reactive,
        effect,
       } from "/node_modules/@vue/reactivity/dist/reactivity.esm-browser.js";
    //   reactive创建一个响应式对象
    //   effect 副作用函数,默认执行一次,数据变化后再次执行
      const state = reactive({ name: "orange", age: 18 });
       effect(() => {
        document.getElementById("app").innerHTML = state.name;
      });
      console.log(state);
      setTimeout(() => {
        state.name = "apple";
      }, 2000);
    </script>

二.reactive与effect实现

1.实现reactive

1.1 get、set基础实现

// reactive.ts
import { isObject } from "@vue/shared";

const mutableHandlers = {
  get(target, key, receiver) {
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    return Reflect.set(target, key, value, receiver);
  },
};

export function reactive(target) {
  if (!isObject(target)) {
    return target;
  }
  const proxy = new Proxy(target, mutableHandlers);
  return proxy;
}

如下图,如果采用target[key]方法,获取aliasName时 不会触发nameget
在这里插入图片描述

1.2 完善重复

const state = { name: "orange", age: 18 };
      const p1 = reactive(state);
      const p2 = reactive(state);
      const p3 = reactive(p1);
      console.log(p1 === p2);
      console.log(p1 === p3);
  • 响应式数据缓存,防止重复代理
    使用mapkey为对象,值为响应式对象,实现p1 === p2
const reactiveMap = new WeakMap(); //内存泄漏
export function reactive(target) {
  if (!isObject(target)) {
    return target;
  }
  const exitProxy = reactiveMap.get(target);
  if (exitProxy) {
    return exitProxy;
  }
  const proxy = new Proxy(target, mutableHandlers);
  reactiveMap.set(target, proxy);
  return proxy;
}
  • 响应式数据标记
    用枚举做标记,响应式对象都有get和set方法,p1初次创建时state没有get和set方法,target[ReactiveFlags.IS_REACTIVE]取值为false,创建p3时,p1响应式,取值为true,直接返回p1
export const enum ReactiveFlags {
  IS_REACTIVE = "__v_isReactive",
}

get(target, key, receiver) {
    if (ReactiveFlags.IS_REACTIVE == key) {
      return true;
    }
    return Reflect.get(target, key, receiver);
  },

export function reactive(target) {
  ...,
  if (target[ReactiveFlags.IS_REACTIVE]) {
    return target;
  }
  const proxy = new Proxy(target, mutableHandlers);
  reactiveMap.set(target, proxy);
  return proxy;
}

2.实现effect

2.1 effect函数

vue2vue3早期的依赖收集采用的都是栈方式存储,vue3后来改为树型数据存储。
effect执行时,把当前effect作为全局的,触发属性的get方法,收集依赖

let activeEffect;
class ReactiveEffect {
  public deps: Array<any> = []; // 判断依赖属性
  public active: boolean = true; // 是否激活
  public parent = undefined; 
  constructor(public fn) {}
  run() {
    if (!this.active) {
      return this.fn();
    }
    try {
      this.parent = activeEffect;
      activeEffect = this;
      return this.fn();
    } finally {
      activeEffect = this.parent;
      this.parent = undefined;
    }
  }
}
export function effect(fn) {
  const _effect = new ReactiveEffect(fn);
  _effect.run();
}

2.2 依赖收集

执行effect时,会触发依赖属性的get方法,在属性的get中进行依赖收集

get(target, key, receiver) {
    if (ReactiveFlags.IS_REACTIVE == key) {
      return true;
    }
    console.log(activeEffect, key);
    track(target,key)
    return Reflect.get(target, key, receiver);
  },
/* 属性变动后 要重新执行run 需要把属性和effect关联起来 收集对象上属性关联的effect
 不能光记录属性,容易两个对象有重名的属性,所以需要带着对象记录
 target为对象
 let mapping = {
    target:{
        name:[effect1,effect2,effect3] 一个属性可以在多个页面使用
    }
 }
 */
const targetMap = new WeakMap();
export function track(target, key) {
  // 如果取值操作没有发生在effect中,则不需要收集依赖
  if (!activeEffect) {
    return;
  }
  // 判断是否已经记录过
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  let dep = depsMap.get(key);
  if (!dep) {
    // 值为一个不重复数组
    depsMap.set(key, (dep = new Set()));
  }
  let shouldTrack = !dep.has(key);
  if (shouldTrack) {
    dep.add(activeEffect);
    activeEffect.deps.push(dep); //同时effect记住当前这个属性
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1407482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML标签(二)

目录 表格标签 表格的主要作用 表格的具体用法 表头单元格标签 表格属性 表格结构标签 合并单元格 合并单元格的方式&#xff1a; 跨行合并&#xff1a; 跨列合并&#xff1a; 列表标签 无序列表 有序列表 自定义列表 表单标签 表单域 表单域的常用属性 表单元素…

SpringBoot整合ElasticSearch实现基础的CRUD操作

本文来说下SpringBoot整合ES实现CRUD操作 文章目录 概述spring-boot-starter-data-elasticsearch项目搭建ES简单的crud操作保存数据修改数据查看数据删除数据 本文小结 概述 SpringBoot支持两种技术和es交互。一种的jest&#xff0c;还有一种就是SpringData-ElasticSearch。根据…

ARM安装与项目结构

1. 安装环境 参考E:\peixunQianrushi\arm\ziliao\FS4412新版&#xff08;学生资料&#xff09;\环境相关资料 这边建议全部默认路径 安装注意事项&#xff1a; 1、在接下来的安装过程中&#xff0c;对于使用win10、win8的操作系统的用户&#xff0c;所有的安装请均以管理员身份…

oracle 19c rac集群管理 ------ 日志管理

oracle 19C rac 数据库的目录结构及日志路径 在Oracle 19c RAC&#xff08;Real Application Clusters&#xff09;集群中&#xff0c;有多个组件和层级生成的日志文件&#xff0c;记录着集群的活动、事件和错误信息&#xff0c;用于故障诊断、性能优化和集群管理。以下是常见…

java抽象工厂实战与总结

文章目录 一、工厂模式&#xff08;三种&#xff09;1.简单工厂模式1.1 概念&#xff1a;1.2 使用场景&#xff1a;1.3 模型图解&#xff1a;1.4 伪代码&#xff1a; 2.工厂方法模式2.1 概念&#xff1a;2.2 使用场景&#xff1a;2.3 模型图解&#xff1a;2.4 伪代码 3.抽象工厂…

动态库和静态库的理解 Linux

其实库文件里面的内容就是函数的实现方法&#xff0c;向我们包含的头文件其实就是函数的生命&#xff0c;而我们编译链接程序时会自动加载库文件&#xff0c;最终形成可执行程序。其实我们在编译链接时不仅仅会将文件的库文件加载进来&#xff0c;其实头文件也是需要加载进来的…

工作进入第八年,还在成长的一年

这一年没有写太多的技术博客&#xff0c;是因为工作内容发生了较大的改变&#xff0c;岗位也发生了调整。随着ChatGPT的爆火&#xff0c;无论从公司的领导层&#xff0c;还是从现场的用户&#xff0c;人工智能算是被彻底颠覆了&#xff0c;每个人对生成式人工智能的期待太高&am…

架构篇24:排除架构可用性隐患的利器-FMEA方法

文章目录 FMEA 介绍FMEA 方法FMEA 实战小结 前面的专栏分析高可用复杂度的时候提出了一个问题&#xff1a;高可用和高性能哪个更复杂&#xff0c;根据墨菲定律“可能出错的事情最终都会出错”&#xff0c;架构隐患总有一天会导致系统故障。因此&#xff0c;我们在进行架构设计的…

Intel Atom + Artix-7 100T FPGA,CompactRIO单板控制器

模拟和数字I/O&#xff0c;RMC&#xff0c;DisplayPort&#xff0c;1.33 GHz双核CPU&#xff0c;1 GB DRAM&#xff0c;4 GB存储容量&#xff0c;Artix-7 100T FPGA&#xff0c;CompactRIO单板控制器 CompactRIO控制器是搭载了实时处理器和用户可编程FPGA的嵌入式控制器。其产…

低代码(Low-Code)技术简化开发难度,快速搭建应用

目录 一、低代码技术定义 二、低代码技术优势 1.提高企业的工作效率 2.降低企业的开发成本 3.提高应用程序和业务流程的质量 三、稳定性和生产率的最佳实践 三、最后 随着数字化时代的到来&#xff0c;低代码&#xff08;Low-Code&#xff09;技术已经成为了企业数字化转…

如何使用Docker本地部署Jupyter Notebook并结合内网穿透实现远程访问

&#x1f4d1;前言 本文主要是Linux下通过使用Docker本地部署Jupyter Notebook并结合内网穿透实现远程访问的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;…

别不信❗️你离数据专家只差一个CDMP证书

1⃣️为什么选择CDMP证书&#xff1f; &#x1f31f;&#x1f31f;亲爱的朋友们&#xff0c;如果你在寻找一个能让你在数据管理领域大展拳脚的证书&#xff0c;那么CDMP&#xff08;Certified Data Management Professional&#xff09;证书就是你的不二之选&#xff01;&#…

JavaScript进阶:WebAPIs重点知识整理2

目录 1 对节点的相关操作 1.1 查找节点 1.1.1 查找节点的父节点 1.1.2 查找节点的子节点 1.1.3 查找节点的兄弟节点 1.2 新增节点&#xff08;先创建&#xff0c;后追加&#xff09; 1.3 克隆节点 1.4 删除节点 2 M 端&#xff08;移动端&#xff09;事件 3 JS清空表…

hadoop集群规划部署

一、集群规划 三台硬件资源&#xff0c;部署hadoop版本&#xff0c;hadoop-3.3.5 &#xff0c;部署后配置文件。 Hadoop配置文件分两类&#xff1a;默认配置文件和自定义配置文件。 hadoop102hadoop103hadoop104HDFS NameNode DataNode DataNode SecondaryNameNode DataN…

深度强化学习Task3:A2C、A3C算法

本篇博客是本人参加Datawhale组队学习第三次任务的笔记 【教程地址】 文章目录 Actor-Critic 算法提出的动机Q Actor-Critic 算法A2C 与 A3C 算法广义优势估计A3C实现建立Actor和Critic网络定义智能体定义环境训练利用JoyRL实现多进程 练习总结 Actor-Critic 算法提出的动机 蒙…

Matlab|基于改进遗传算法的储能选址定容(可任意设定储能数量)

目录 主要内容 部分代码 结果一览&#xff08;以3个储能为例&#xff09; 下载链接 主要内容 该模型采用改进遗传算法优化配电网系统中储能选址位置和容量&#xff0c;程序以IEEE33节点系统为分析对象&#xff0c;以网损最小为目标&#xff0c;采用matpower实现系…

谷粒商城-微服务架构图

整体架构 分布式划分图

蓝牙运动耳机什么牌子的好?蓝牙运动耳机品牌排行榜前十名

​运动耳机是耳机中使用场景最广泛的一类&#xff0c;特别适合户外运动、健身和骑行等场景。在众多运动耳机中&#xff0c;哪一款更值得入手呢&#xff1f;今天我将向大家推荐几款相当不错的运动耳机&#xff0c;它们不仅音质上乘&#xff0c;还能满足不同运动场景的需求。 1.…

ozon促销活动100+店铺如何多店铺批量加入活动产品?ozon促销产品怎么删除?

很多Ozon卖家为实现店铺引流&#xff0c;会参与许多官方促销活动&#xff0c;如果每个活动都需要单独管理&#xff0c;会消耗不少的时间成本&#xff0c;操作起来也会非常困难。 尤其是随着运营店铺数量的增加&#xff0c;多个店铺多个促销活动来回切换管理&#xff0c;不仅耗…

modbus poll测试工具测试modbus tcp与PLC设备连接使用方法

socket默认端口是502&#xff0c;socket连上之后&#xff0c; 按照modbuspoll工具设置的读写参数 生成的RTU命令格式去组装读PLC的设备数据 modbuspoll工具配置&#xff0c;以v9.9.2中文破解版为例&#xff1a; 首先点连接菜单&#xff08;connection&#xff09;建立连接&…