响应式原理 之 vue2 vue3

news2024/11/18 1:34:48

目录

一、响应式概念

二、响应式函数的实现 watchFn

三、响应式依赖的收集

四、监听对象的变化

1. vue2

2. vue3

五、对象的依赖管理

1. 图解

2. 代码

六、响应式完整代码


一、响应式概念

  • m有一个初始化的值,有一段代码使用了这个值
  • 那么在m有一个新的值时,这段代码可以自动重新执行

这样一种可以自动响应数据变量的代码机制,就称之为是响应式的 

二、响应式函数的实现 watchFn

  • 封装一个新的函数watchFn
  • 凡是传入到watchFn的函数,就是需要响应式的
  • 其他默认定义的函数都是不需要响应式的
// 需要收集依赖的函数
let reactiveFn = null;
function watchFn(fn) {
  reactiveFn = fn;
  fn();
  reactiveFn = null;
}

// 使用
watchFn(function foo() {
  console.log('foo => name :', obj.name);
  console.log('foo => age : ', obj.age);
});

三、响应式依赖的收集

设计一个类,这个类用于管理某一个对象的某一个属性的所有响应式函数

// 1. 创建一个Depend类
class Depend {
  constructor() {
    // 2. 创建一个数组,用来存储依赖
    this.reactiveFns = new Set();
  }
  // 3. 用来通知Depend类中的依赖执行
  notify() {
    this.reactiveFns.forEach((fn) => {
      fn();
    });
  }
  // 4. 用来添加依赖
  addDepend(fn) {
    if (fn) {
      this.reactiveFns.add(fn);
    }
  }
  // 5. 也可以这么添加
  depend() {
    if (reactiveFn) {
      this.reactiveFns.add(reactiveFn);
    }
  }
}

四、监听对象的变化

1. vue2

通过 Object.defineProperty的方式

// 拦截对象属性,使对象变成响应式
function reactive(obj) {
  Object.keys(obj).forEach((key) => {
    let value = obj[key];
    // 找到对应的obj对象的key对应的dep对象
    const dep = getDepend(obj, key);

    // 设置set、get拦截
    Object.defineProperty(obj, key, {
      set(newValue) {
        value = newValue;
        // 修改数据后进行通知
        dep.notify();
      },
      get() {
        // 增加依赖
        // dep.addDepend(reactiveFn);
        dep.depend();
        return value;
      }
    });
  });
  return obj;
}

2. vue3

通过new Proxy的方式

function reactive(obj) {
  // 1. 创建代理对象
  const objProxy = new Proxy(obj, {
    set(target, key, newValue, receiver) {
      // target[key] = newValue;
      // 运用Reflect反射
      Reflect.set(target, key, newValue, receiver);
      const dep = getDepend(target, key);
      // 通知变化
      dep.notify();
    },
    get(target, key, receiver) {
      const dep = getDepend(target, key);
      // 收集依赖
      dep.depend();
      // return target[key];
      return Reflect.get(target, key, receiver);
    }
  });
  return objProxy;
}

五、对象的依赖管理

1. 图解

2. 代码

// 封装一个函数: 负责通过obj的key获取对应的Depend对象
const objMap = new WeakMap();
function getDepend(obj, key) {
  // 获取外层对象的map
  let map = objMap.get(obj);
  if (!map) {
    map = new Map();
    objMap.set(obj, map);
  }

  let dep = map.get(key);
  if (!dep) {
    dep = new Depend();
    map.set(key, dep);
  }

  return dep;
}

六、响应式完整代码

// 1. 创建一个Depend类
class Depend {
  constructor() {
    // 2. 创建一个数组,用来存储依赖
    this.reactiveFns = new Set();
  }
  // 3. 用来通知Depend类中的依赖执行
  notify() {
    this.reactiveFns.forEach((fn) => {
      fn();
    });
  }
  // 4. 用来添加依赖
  addDepend(fn) {
    if (fn) {
      this.reactiveFns.add(fn);
    }
  }
  // 5. 也可以这么添加
  depend() {
    if (reactiveFn) {
      this.reactiveFns.add(reactiveFn);
    }
  }
}

// 需要收集依赖的函数
let reactiveFn = null;
function watchFn(fn) {
  reactiveFn = fn;
  fn();
  reactiveFn = null;
}

// 封装一个函数: 负责通过obj的key获取对应的Depend对象
const objMap = new WeakMap();
function getDepend(obj, key) {
  // 获取外层对象的map
  let map = objMap.get(obj);
  if (!map) {
    map = new Map();
    objMap.set(obj, map);
  }

  let dep = map.get(key);
  if (!dep) {
    dep = new Depend();
    map.set(key, dep);
  }

  return dep;
}

// 拦截对象属性,使对象变成响应式
/**
 * vue2 : Object.defineProperty
 */
function reactive(obj) {
  Object.keys(obj).forEach((key) => {
    let value = obj[key];
    // 找到对应的obj对象的key对应的dep对象
    const dep = getDepend(obj, key);

    // 设置set、get拦截
    Object.defineProperty(obj, key, {
      set(newValue) {
        value = newValue;
        // 修改数据后进行通知
        dep.notify();
      },
      get() {
        // 增加依赖
        // dep.addDepend(reactiveFn);
        dep.depend();
        return value;
      }
    });
  });
  return obj;
}

/**
 * vue3 : new Proxy
 */
function reactive3(obj) {
  // 1. 创建代理对象
  const objProxy = new Proxy(obj, {
    set(target, key, newValue, receiver) {
      // target[key] = newValue;
      // 运用Reflect反射
      Reflect.set(target, key, newValue, receiver);
      const dep = getDepend(target, key);
      // 通知变化
      dep.notify();
    },
    get(target, key, receiver) {
      const dep = getDepend(target, key);
      // 收集依赖
      dep.depend();
      // return target[key];
      return Reflect.get(target, key, receiver);
    }
  });
  return objProxy;
}

/**
 * 业务代码
 */

const obj = reactive({
  name: 'star',
  age: 18
});

watchFn(function foo() {
  console.log('foo => name :', obj.name);
  console.log('foo => age : ', obj.age);
});

watchFn(function bar() {
  console.log('bar => age : ', obj.age + 10);
});

obj.name = 'coder';
// obj.age = 20;

const user = reactive({
  name: 'aaa'
});
watchFn(function () {
  console.log('baz:', user.name);
});
user.name = 'bbb';

 

 

 

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

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

相关文章

热门探讨丨SaaS软件是否正在“毁掉”数字化转型企业?

——当它浮出水面,才能看到水下的错落。 截至2021年末,我国企业的数量达到4842万户,增长1.7倍,其中99%以上都是中小企业。 根据调查,在数字化转型浪潮中,我国有超过70%的企业对数字化转型处于积极态度&am…

网络部署运维实验(pat 端口映射含命令)

♥️作者:小刘在这里 ♥️每天分享云计算网络运维课堂笔记,疫情之下,你我素未谋面,但你一定要平平安安,一 起努力,共赴美好人生! ♥️夕阳下,是最美的,绽放,…

Vue JSX 上手指南

JSX 简介 JSX Javascript XML。在 Javascript 里写 XML,同时拥有 Javascript 的灵活性和 HTML 的语义化。 Template vs JSX template 是 Vue 的默认写法,也更推荐。因为 template 语法是固定的,Vue 在编译层面为它做了很多静态标记的优化…

MySQL简介及常用引擎介绍

MySQL 由 My 和 SQL 组成,其中的 SQL 部分即为:Structured Query Language,意为结构化查询语⾔,是访问数据库的最常⻅的标准化语⾔。 MySQL 是一款优秀的、开源的数据库管理系统,同时 MySQL 也是一款可移植的数据库&a…

【WSL】[02] windows subsytem linux 配置和使用

第【2】章前言: 上一篇我们已经安装好了ubuntu在WSL下的GUI的桌面:我们现在继续进一步进行配置工作。同时也把,运用WSL的流程摸一把,以备后用。 1 在windows的PowerShell终端进行配置和查询: 1.1 查询所有支持的可以…

极小尺寸,超低功耗,高度集成,天翼物联NB-IoT云芯模组

数字经济时代,物联网已融入我们的日常生活。比如,智能抄表代替人工上门抄表,运动手环实时传输心率情况,烟感报警器远程告警……这些高科技背后都有一颗小而强大的芯片在支撑。 天翼物联云芯模组依托天翼物联网平台(AIo…

制造业项目管理软件如何帮助企业做好项目管理?

项目产品、采购、销售、BOM变更管理不全面; 项目进度追踪管理难; 项目进度款管不清、项目尾款管不到; … … 而以上仅仅是生产制造企业所面临的项目管理问题的“冰山一角”。 采用“项目制”生产模式的制造企业,需要做到项目…

理解XaaS(SaaS、Baas、Paas、Iaas)

XaaS : XX as a Service 参考原文 以电商来举例 一套电商系统需要的开发资料 产品经理:设计电商系统 前端工程师:开发页面 后端工程收:开发后端 运维工程师:搭建环境,部署到服务器,负责后期维护等 服务器…

指南解读:急性心力衰竭中国急诊管理指南(2022)

心力衰竭(heart failure,HF 简称心衰)是由于心脏结构和 / 或功能异常导致心室充盈和/或射血能力受损的一组临床综合征,其病理生理学特征为肺淤血和/或体循环淤血、伴或不伴有组织器官低灌注,主要临床表现为呼吸困难、乏…

美容门店信息化管理系统该如何搭建?不妨参考一下百数

随着人们的生活水平越来越高,人们在解决了温饱问题之后有了更多的追求。其中美容正在成为不少人新的必做项目, 迎合了人们对于爱美的需求。目前我国美容机构市场规模已超过4500亿元,行业从业人员超过3000万。据国家工商联统计数字显示&#x…

JS_fetch请求数据

1、axios 比如常用的需求中,想要请求A接口的数据再去请求B接口的数据,axios的做法是需要先请求A接口,然后在A接口的成功的回调函数里面去请求B接口。 fetch使用的情况并不多,主要是由于存在兼容性问题,在企业中就不会…

南大通用数据库-Gbase-8a-学习-29-常用函数介绍

一、测试环境 名称值cpu12th Gen Intel Core™ i7-12700H操作系统CentOS Linux release 7.9.2009 (Core)内存3G逻辑核数2Gbase-8a数据库版本9.5.3.27 二、函数介绍 1、HEX (1)说明 将数字或字符串转换成十六进制形式。 (2)例…

2022年我去过最喜欢的16座城市

欢迎关注「苏南下」分享我的旅行、摄影心得感悟2022 年我去了 16 个城市地区,拍了 40条旅行短片。又是很久没更新,2022 年过得好快,马上就是 2023 了。如果用一个关键词来总结我这一年,我觉得可以是:运气好。从年初 1 …

P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布

题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样,则不分胜负。在《生活大爆炸》第二季第 8 集中出现了一种石头剪刀布的升级版游戏。 升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势: 斯波克:《星际迷航》主角…

捷报频传 | Bonree ONE获2022科技赋能金融业场景金融建设突出贡献奖

近日,由《金融电子化》杂志社主办的“2022第十三届金融科技应用创新奖”评选结果正式揭晓。本次评奖围绕项目先进性、项目创新点、项目对推动数字化转型及提升服务能力的作用、社会及经济效益等维度展开。博睿数据一体化智能可观测平台Bonree ONE再次脱颖而出&#…

【 Tomcat服务器】

文章目录二、Web服务器2.1 概念2.1.1 什么是Web2.1.2 什么是Web服务器2.2 常见Web服务器2.3 Tomcat服务器2.3.1 Tomcat的下载2.3.2 Tomcat的安装2.3.3 Tomcat的目录结构2.3.4 Tomcat的启动2.3.5 Tomcat的停止运行所需jar包2.3.5 Tomcat的停止2.3.6 修改Tomcat端口号2.3.7 项目部…

有关文件IO操作的错误(error)提示

在编程中,我们一般都是要讲究一定的使用规范的。按照一定的规范编写代码,返回运行提示,在代码运行出现问题时能方便我们能快速的定位到问题的所在。 特别是在使用库函数或者系统调用的API的时候,我们最好也要关注函数的返回值所能…

INTENT2022--一道包含12个反调试反虚拟机操作的ctf题解

作者:selph 从一道Re题学习12种反调试反虚拟技术 题目:AntiDebuggingEmporium 来源:INTENT CTF 2022 Re 这个题目很有意思,里面出现了总共12个反调试反虚拟机的操作,本文内容分两部分,前部分是题解&…

六西格玛奠基人之张驰染阳杂记

中招第1天: 上周四从外地出差回来,落地就觉得有点全身乏力,晚上开始发冷,预感可能中招了。大数据预测深圳第一波疫情高峰发生在12月20日,陆续接到深圳同事亲友中招的消息,感慨真是神预测。 因为头晕目眩&…

LeetCode419.甲板上的战舰

LeetCode刷题记录 文章目录📜题目描述💡解题思路⌨C代码📜题目描述 给你一个大小为 m x n 的矩阵 board 表示甲板,其中,每个单元格可以是一艘战舰 X 或者是一个空位 . ,返回在甲板board上放置的 战舰 的数量…