【源码解读】VUE2.0和VUE3.0响应式区别?

news2025/1/31 15:45:37

数据劫持的目的

VUE2.0和VUE3.0实现响应式的底层逻辑,是对数据做劫持,为什么要劫持数据呢?是因为,劫持数据后才可以,在更改数据同时对页面进行重新渲染,从而达到响应式。

VUE3.0响应原理

VUE3.0使用了ES6 proxy做代理,proxy是用于创建一个对象的代理,可实现基本操作的拦截和自定义。简而言之,proxy可以在数据做操作时,进行拦截,其基础语法是:const p = new Proxy(target,handler) target为要操作的obj,handler是数据的set,get操作方法。
举个例子:

   let test = reactive({ a: 1, b: 2 });
    function reactive(target) {
      const testProxy = new Proxy(target, {
        get(target, key, receiver) {
          //拦截后可以做很多事情,比如视图渲染
          const res = Reflect.get(target, key, receiver); //等同于res=target[key]
          console.log("响应式获取:" + res);
          return res;
        },
        set(target, key, value, receiver) {
        //拦截后可以做很多事情,比如视图渲染
          const res = Reflect.set(target, key, value, receiver); //等同于设置target[key]=value
          console.log("响应式设置:" + res);
        }
      });
      return testProxy;
    }

    test.a;
    test.a = 3;

运行结果:运行结果
以上例子是reactive核心思想,其本质也就是对对象进行了代理,并返回代理对象。在get、set方法中使用了Reflect对象,**注意:这是个对象,并不是方法。**官方解释,Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。那为什么在这里使用Reflect对象呢?因为,Reflect与Proxy的get(),set()方法一一对应,一个Proxy的get方法对应一个Reflect.get()方法,如果使用obj.defineProperty方法修改两次同一属性的,就会报错。JS又是单线程的,报错后不会继续执行下面代码,则框架的健壮性就会很差。

    let test = { a: 1, b: 2 };
    Object.defineProperty(test, "c", {
      get() {
        return 7;
      }
    });
    Object.defineProperty(test, "c", {
      get() {
        return 99;
      }
    });

在这里插入图片描述
但如果使用Reflect,就不会报错,会将数据反射出去。

    let test = { a: 1, b: 2 };

    const t1 = new Proxy(test, {
      get() {
        return 7;
      }
    });
    console.log("t1.c=" + t1.c);
    const t2 = new Proxy(test, {
      get() {
        return 99;
      }
    });
    console.log("t2.c=" + t2.c);

在这里插入图片描述

VUE2.0响应式原理

vue2.0响应式是通过Object.defineProperty方法,对属性进行劫持。官方文档解释,Object.defineProperty() 静态方法会直接在一个对象上定义一个新属性,或修改其现有属性,并返回此对象。基础语法是:

Object.defineProperty(obj, prop, descriptor)

obj
要定义属性的对象。

prop
一个字符串或 Symbol,指定了要定义或修改的属性键。

descriptor
要定义或修改的属性的描述符。

可以看出,Object.defineProperty是需要对对象里的每个属性,一一进行设置或者监听。在VUE2中,也是使用该核心思想,但此方法只可以对对象进行监听,数组Array监听则采用对数组原型方法重写,进行数据拦截。

  • 对象Object监听
let test = { a: 1, b: 2 };
    observer(test);
    function observer(obj) {
      let testKeys = Object.keys(test);
      testKeys.map((key) => {
        defineProperty(obj, key, obj[key]);
      });
    }
    function defineProperty(obj, key, val) {
      Object.defineProperty(obj, key, {
        get() {
          console.log("响应式获取:" + val);
          return val;
        },
        set(newval) {
          console.log("响应式设置:" + key + "=" + newval);
          val = newval;
        }
      });
    }
    console.log(test.a);
    //响应式获取:1
    // 1
    test.a = 5;
    //响应式设置:a=5
    console.log(test.a);
     //响应式获取:5
    // 5

源码核心思想如上,会建立一个observer监听对象,并在defineProperty中实现对每个属性的数据拦截。
如果是多层对象嵌套,可通过递归方式,对属性中数据做深入拦截。

let test = { a: 1, b: { c: 2, d: 3 } };
    observer(test);
    function observer(obj) {
      if (typeof obj !== "object") return;
      let testKeys = Object.keys(obj);
      testKeys.map((key) => {
        defineProperty(obj, key, obj[key]);
      });
    }
    function defineProperty(obj, key, val) {
      if (typeof val === "object" || val !== null) {
        observer(val);
      }
      Object.defineProperty(obj, key, {
        get() {
          console.log("响应式获取:" + val);
          return val;
        },
        set(newval) {
          console.log("响应式设置:" + key + "=" + newval);
          val = newval;
        }
      });
    }
  • 数组Array监听
    数组的数据劫持,主要是对数组中,会影响数组结构和内容的方法,在原型链上重写。这些方法包括push、pop、shifit等,这是因为数组结构发生变化,需要视图进行同步更新,所以需要对这些方法进行劫持。
 const customMethods = [
      "push",
      "pop",
      "shifit",
      "unshift",
      "splice",
      "sort",
      "reverse"
    ];
    const originArrMethods = Array.prototype, //获取Array上的原型对象,包含所有方法
      arrMethods = Object.create(originArrMethods); //创建一个和原型方法一样的对象
    customMethods.map((m) => {
      //重写方法
      arrMethods[m] = function () {
        const args = Array.prototype.slice.call(arguments), //将入参处理为数组
          rt = originArrMethods[m].apply(this, args); //将this指向改为原始Array
        let newArr;
        switch (m) {
          case "push":
          case "unshift":
            newArr = args;
            break;
          case "splice": //splice(start,end,新值)
            newArr = args.slice(2); //取新值
            break;
          default:
            break;
        }
        return rt;
      };
    });

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

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

相关文章

适配器模式:如何让不兼容的接口变得兼容

在软件开发中,我们经常会遇到这样的情况:我们需要使用一个现有的类或者接口,但它与我们系统的目标接口不兼容,而我们又不能修改它。这时候,我们该怎么办呢?大多数情况下我们都可以使用适配器模式来解决这个…

hutool的HttpRequest.post的使用-包括上传文档等多个传参【总结版本】

首先hutool已经为我们封装好了远程调用的接口,我们只要将对应的传参和方式对应填写即可 hutool官方文档 1实际应用 post 常见的使用json传参,contend type为application/json RequestMapping("login") ResponseBody public static String s…

中国人大与加拿大女王大学中外合作办学金融硕士——学习是你世界的圆吗

有人说,可以把每个人的世界比作一个圆,学习是半径,半径越大,拥有的世界就越广阔。你认同这种观点吗,金融领域的你有想过拓宽知识的边界吗?来人大女王中外合作办学金融硕士项目在职读研,超越自我…

【多线程】线程 与 进程

线程 与 进程 一. 进程1. 操作系统2. 什么是进程/任务(Process/Task)3. 为什么要有进程3. 操作系统怎样管理进程4. 进程控制块抽象(PCB Process Control Block)5. 进程调度6. 内存分配 —— 内存管理(Memory Manage)7. 进程间通信…

【C++】匿名对象 | explicit | static静态成员 | 友元

匿名对象 有些情况下,我们创建变量只是临时用一用,以后不会再用到了。这时,如果创建变量还要起名字,是不是很多余? 对此,C引入了 匿名对象 供临时使用。 例如:如果我们创建对象slt&#xff0c…

什么是主动元数据管理?有何优势?

元数据是描述数据的数据,它提供数据的来源、含义、位置、所有权和创建等信息,主要用于跟踪、分类和分析。 元数据管理则是对元数据的创建、存储、整合、控制的一整套流程,是数据治理过程的一部分,能够支持基于元数据的相关需求和…

NFT Insider#106:The Sandbox 与 Light Matrix 以及鲁比尼拳击场达成战略合作

引言:NFT Insider由NFT收藏组织WHALE Members、BeepCrypto联合出品,浓缩每周NFT新闻,为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周报将从NFT市场数据,艺术新闻类,游戏新闻类,虚拟世界类&#…

Android12之解析/proc/pid进程参数(一百六十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

python通过tkinter制作词云图工具

一、基本功能 1.采取上传文本文档(仅支持.txt格式)的方式统计词频 2.背景图形样式可选择已经设定好的,也可选择本地上传的(支持.png .jpg .jpeg格式) 3.本地上传的图片需要进行抠图处理,并将抠图结果保存…

大乘数法 -Java

题目链接:https://www.nowcoder.com/practice/c4c488d4d40d4c4e9824c3650f7d5571?tpId196&tqId37177&rp1&ru/exam/company&qru/exam/company&sourceUrl%2Fexam%2Fcompany&difficultyundefined&judgeStatusundefined&tags&titl…

SAP 批量删除变式

使用事务码se38进入变式,通过搜索帮助选中一个变式,点击显示。 点击目录,会显示所有的变式名称。 会显示当前程序的所有变式,然后点击删除按钮,选择需要删除的变式。

CVPR2023 RIFormer, 无需TokenMixer也能达成SOTA性能的极简ViT架构

编辑 | Happy 首发 | AIWalker 链接 | https://mp.weixin.qq.com/s/l3US8Dsd0yNC19o7B1ZBgw project, paper, code Token Mixer是ViT骨干非常重要的组成成分,它用于对不同空域位置信息进行自适应聚合,但常规的自注意力往往存在高计算复杂度与高延迟问题。…

FP130A 封装SOT23-5L 轨道电流测量IC

FP130A 封装SOT23-5L 轨道电流测量IC 一般说明 FP130A是一种宽共模范围高侧轨电流测量IC。它适用于电源系统,如电池充电器或开关电源的应用。它包括一个差分输入放大器和一个具有发射极输出的NPN晶体管。有三个外部电阻,轨道电流信号可以很容易地转换为I…

VR软件与管理后台的协议(微信扫码)

一、微信扫码登录 1、设计流程: ①、VR软件界面生成二维码,二维码中携带跳转小程序的链接及设备号、公司ID;用户通过扫码进入微信小程序点击界面一键启动,开始完善个人信息。 ②、用户点击一键启动,用户信息的授权状态…

入门人工智能 —— 学习 python 使用 IDE :vscode 完成编程 (2)

入门人工智能 —— 学习 python 使用 IDE :vscode 完成编程 (2) 安装和配置 VSCode创建和运行 Python 代码使用 VSCode 的调试功能 在上一篇文章中,介绍了如何入门人工智能编程,并开始了学习 Python 编程语言的基础知识…

润和软件HopeStage与上海瑞美云LIS系统管理软件完成产品兼容性互认证

近日,江苏润和软件股份有限公司(以下简称“润和软件”)HopeStage 操作系统与上海瑞美电脑科技有限公司(以下简称“上海瑞美”)瑞美云LIS系统管理软件完成产品兼容性测试。 测试结果表明,企业级通用操作系统…

NFTScan NFT API 在 NFTFi 开发中的应用

NFTFi 是“NFT”和“Finance”的缩写,旨在“增加 NFT 流动性,提供现金流”,NFTFi 是为 NFT 提供金融实用性的去中心化协议和应用程序的新兴生态系统,及使用 NFT 作为基础层在其上建设经济基础设施。 在实践中,NFTFi 协…

yolov7中Concat之后加注意力模块(最复杂的情况)

1、common.py中找到Concat模块,复制一份 2、要传参进来,dim通道数 3、然后找yolo.py模块,添加 4、yaml里替换 5、和加的位置也有关系

20 Spring Boot整合Redis

一、Redis简介 简单来说 Redis 就是一个使用 C 语言开发的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。 另外,Redis 除…

c++ vs2019 cpp20 规范,set源码分析

(1)set模板和map模板都是继承于一个父类 所以没有再详细注释。维持红黑树主要的功能都在父类_Tree里了,比如节点的添加,删除,查找。父类红黑树的操作,并不依赖于特定的数据类型。做到了父类模板的通用性。…