vue3源码解析——ref和reactive定义响应式的区别

refreactive 是 Vue 3.0 中用于定义响应式数据的两个新 API。它们有以下区别:

ref 定义单个响应式数据

  • 数据类型可以是任意类型。它通常用于定义原始数据类型为响应式数据。
  • 返回一个响应式对象,该对象包含一个 .value 属性,可用于获取和设置数据。
  • 底层采用Object.defineProperty()实现

reactive 定义多个响应式数据

  • 数据类型必须是对象
  • 返回一个响应式对象,必须使用响应式对象来获取属性和设置数据
  • 底层采用的是new Proxy()

reactive源码解析

reactive 函数可以用来创建一个响应式的对象,它会在内部使用 Proxy 对对象的 getter 和 setter 进行拦截,从而实现对数据的依赖收集和通知更新。

在vue3源码里,reactive的实现在core-main\packages\reactivity\src\reactive.ts文件中

 reactive是一个函数,接收传入的target对象。返回一个createReactiveObject方法。

 reactive的核心逻辑在createReactiveObject方法中。

  • 首先校验传入的target必须是对象;
  • 通过new Proxy创建一个代理对象,根据对象的类型走不同的handler处理逻辑;
  • 返回代理对象proxy

proxy是怎么代理的?

手写一个简易的reactive,看下handler里是什么

  let handler = {
    //拦截整个对象,访问对象的属性时get拦截器触发
    get(target, key) {
      let value = target[key];
      if (typeof value === "object") {
        //如果访问的对象属性还是对象,进行递归
        return new Proxy(value, handler);
      }
      return value;
    },
    //拦截整个对象,当修改对象的属性的时候set拦截器会触发
    set(target, key, value) {
      target[key] = value;
    },
  };
  function reactive(target) {
    return new Proxy(target, handler);
  }

  let obj = { name: "jw", age: 30, n: [1, 2, 3, 4, 5] };
  //拿到obj的代理对象proxyObj
  const proxyObj = reactive(obj);
  //不访问obj,访问代理对象proxyObj
  console.log(proxyObj.name); //触发get拦截器
  proxyObj.age = 31; //触发set拦截器
  proxyObj.name = 100; //设置一个不存在的属性

handler 对象中,可以定义多个方法来控制代理对象的行为。在响应式reactive中,handler主要用了get和set两个方法。(是不是很熟悉?Object.defineProperty也有get和set方法,只不过是针对属性的;proxy这个是针对对象的,所以不一样哦

  • get(target, property, receiver):拦截对象属性的读取操作,当访问代理对象的属性时会触发该方法。
  • set(target, property, value, receiver):拦截对象属性的设置操作,当给代理对象的属性赋值时会触发该方法。

 这样每次你通过访问代理对象proxyObj访问的属性,都会被handler的get方法拦截,进而收集依赖。对proxyObj修改属性值的时候,被handler的set方法拦截,进行依赖更新。

注意:你不是操作原始对象obj,而是操作的代理对象proxyObj

ref源码原理

在解析源码之前,先思考一个问题

为什么ref定义后的对象可以通过.value 访问和设置数据?

你有没有想过,为什么使用ref定义的对象或基本类型,在js里写的时候都需要用.value啊。

这里不得不提到一个特殊的名称——typeScript类的属性访问器:get和set属性。

get 和 set 属性是一种在类中定义属性的方式,可以用来实现类的属性的读取和写入操作。基本语法如下:

class MyClass {
  private _propertyName: any;
  get propertyName(): any {
    console.log("读取数据,自动进入get方法");
    return this._propertyName;
  }
  set propertyName(value: any) {
    this._propertyName = value;
    console.log("更新数据,自动进入set方法");
  }
}

在上面的例子中,propertyName 是一个类的属性,它使用 get 和 set 属性定义在 MyClass 类中。在类的外部,可以使用点操作符来读取和写入 propertyName 属性,例如:

const myObject = new MyClass();
myObject.propertyName = 'hello';//更新
console.log(myObject.propertyName); //访问

执行结果

当在类的外部读取 propertyName 属性时,会调用 propertyName 的 get 属性,返回 _propertyName 的值。当在类的外部写入 propertyName 属性时,会调用 propertyName 的 set 属性,将值赋给 _propertyName 属性。

为什么在类里这样写可以触发get和set呢?

这个是ts提供了一种写法,当我们在控制台使用tsc进行编译成.js文件后。

得到如下代码

var MyClass = /** @class */ (function () {
    function MyClass() {
    }
    Object.defineProperty(MyClass.prototype, "propertyName", {
        get: function () {
            console.log("读取数据,自动进入get方法");
            return this._propertyName;
        },
        set: function (value) {
            this._propertyName = value;
            console.log("更新数据,自动进入set方法");
        },
        enumerable: false,
        configurable: true
    });
    return MyClass;
}());
var myObject = new MyClass();
myObject.propertyName = "hello";
console.log(myObject.propertyName);

可以看到,最终propertyName编译成一个属性,并且通过Object.defineProperty进行了get和set方法的重写。 

OK,先说到这,带着这个理解去看ref的源码

vue3中ref实现

 在vue3源码里,ref的实现也是在reactivity文件夹下:core-main\packages\reactivity\src\ref.ts

 ref是一个函数,接收一个unknown类型的参数value;

返回一个createRef方法,第一个参数是value,第二个参数是false.

 createRef最终返回一个RefImpl实例对象。在RefImp类里,首先通过构造函数,创建一个_value对象。toReactive根据value类型,如果是对象的话,用reactive方法处理对象。

RefImpl类还定义了get和set方法,在类里使用"get空格value"这种方式很少见。这其实是ts类的属性访问器写法,当使用 .value 属性来获取或设置数据时,会自动调用 getset 访问器函数。

在底层,这种写法会被编译成 Object.defineProperty,这是 JavaScript 中用于定义对象属性的原生方法。使用 Object.defineProperty 可以定义对象的属性的特性,如可枚举性、可配置性、可写性等,并可以为属性设置 getset 函数,从而实现属性的访问器功能。(前面已经介绍过原理了

toReactive方法做了什么?

toReactive就是判断ref传入的是不是对象,如果是对象,去调用reactive方法将对象变成响应式的。ref这个老六,就是万精油,啥都能处理。

 思考:为什么你更习惯使用ref而不是reactive?

在Vue3中,无论是基本类型还是复杂类型的响应式数据,都推荐使用ref来创建。这样做的好处是,你可以统一使用.value属性来访问和修改响应式数据的值,而不需要在基本类型和复杂类型之间切换使用方式。

  1. 简单性ref 提供了一种简单的方式来定义响应式对象,只需传入初始值即可。相比之下,reactive 需要将整个对象传入,稍显繁琐。对于单个变量或简单数据,使用 ref 更加直观和方便。

  2. 透明性ref 返回的是一个包装过的对象,可以通过 .value 访问其值,这种包装使得数据访问更加明确和直观。而 reactive 返回的是原始对象,需要通过代理访问属性,有时会增加代码的复杂性。(复杂的代码谁想写啊)

  3. 性能:在某些情况下,ref 比 reactive 更高效。因为 ref 包装的是基本类型数据,而 reactive 包装的是对象,对于简单数据类型,ref 的性能可能更好。

  4. 推荐度:Vue 3 官方文档和社区更倾向于推荐使用 ref,因为它更简单、更直观,适用于大多数场景。而 reactive 更适合处理复杂的对象或数据结构。

尽管程序员通常建议使用 ref,但在实际开发中,根据具体情况选择合适的方式是更为重要的。对于简单的数据,使用 ref 可能更加方便和直观;而对于复杂的对象或数据结构,使用 reactive 可能更合适。

 看到这的,给我来波666,太烧脑了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在ScadaFramework里配置采集Modbus设备

ScadaFramework是一个实现SCADA功能的软件工具,经过简单配置之后,即可采集设备数据,并进行存储、监控、组态可视化,并可将数据上传至平台。 本文将介绍如何在ScadaFramework中配置,以采集Modbus协议的设备。 一、添加链…

win10配置CLion2022+ubuntu20.04远程部署

背景 在博文ubunut搭建aarch64 cuda交叉编译环境记录中,使用的ubuntu20.04虚拟机安装eclipse来交叉编译aarch64的程序,然后发送到jetson板子上执行。开发一段时间后发现eclipse IDE使用起来不太便捷,因此,考虑使用CLion IDE&…

分库分表 ——12 种分片算法

目录 前言 分片策略 标准分片策略 行表达式分片策略 复合分片策略 Hint分片策略 不分片策略 分片算法 准备工作 自动分片算法 1、MOD 2、HASH_MOD 3、VOLUME_RANGE 4、BOUNDARY_RANGE 5、AUTO_INTERVAL 标准分片算法 6、INLINE 7、INTERVAL COSID 类型算法 …

鸿蒙(HarmonyOS)ArkTs语言基础教程开发准备

本文档适用于HarmonyOS应用开发的初学者。通过构建一个简单的具有页面跳转/返回功能的应用(如下图所示),快速了解工程目录的主要文件,熟悉HarmonyOS应用开发流程。 在开始之前,您需要了解有关HarmonyOS应用的一些基本概…

【洛谷】P9240 [蓝桥杯 2023 省 B] 冶炼金属

题目链接 P9240 [蓝桥杯 2023 省 B] 冶炼金属 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路 这道题可以用数学的方法去做,但是我想不到😇有兴趣的可以去看看数学的题解 比较简单的思路就是二分查找,轻松简单不费脑,带你…

pygame的搭建

pygame的介绍与环境搭建 Pygame模块 安装 WindowsR打开命令窗口,输入: pip install pygame 或者安装指定版本 pip install pygame 2.3.0常用模块 在Pygame框架中有很多模块,官方网址pygame news 。 其中最常用模块的具体说明如下表所示…

【Java SE】深入理解static关键字

🥰🥰🥰来都来了,不妨点个关注叭! 👉博客主页:欢迎各位大佬!👈 文章目录 1.static关键字1.1 static的概念1.2 static的作用1.3 static的用法1.3.1 static修饰成员变量1.3.2 static修饰…

Ubuntu20安装python3.10

1、添加 deadsnakes PPA 到源列表 add-apt-repository ppa:deadsnakes/ppa apt update 2、安装 apt install python3.10 3设置默认版本为 Python3.10 查看所有python版本 ls -l /usr/bin/python* update-alternatives --install /usr/bin/python3 python3 /usr/bin/pytho…

居里亚斩获金定奖,第13届广州定制家居展见证品牌实力飞跃

2024年3月30日,第13届广州定制家居展圆满落幕。 作为年度首场定制家居专业大展,第13届广州定制家居展的展览规模达到了100,000平方米,吸引了超过350,000人次的关注,以及800多家参展企业的参与。 在百花齐放的定制家居品类中&#…

物联网监控可视化是什么?部署物联网监控可视化大屏有什么作用?

随着物联网技术的深入应用,物联网监控可视化成为了企业数字化转型的关键环节。物联网监控可视化大屏作为物联网监控平台的重要组成部分,能够实时展示物联网设备的运行状态和数据,为企业管理决策和运维监控提供了有力的支持。今天,…

第45期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区,集成了生成预训练Transformer(GPT)、人工智能生成内容(AIGC)以及大语言模型(LLM)等安全领域应用的知识。在这里,您可以找…

使用Git处理Github中提交有冲突的pull request

前言: 为什么要写这篇文章,因为前段时间有一个开源的github中的项目有一个朋友提交了一个pr看了下是帮忙优化了下代码(十分感谢这位网友)。但是他提交的pr刚好和我的项目有许多的冲突导致无法自动合并,在github中提示…

[蓝鲸有奖征文]蓝鲸6.1 CMDB 事件推送的开源替代方案

本文来自腾讯蓝鲸智云社区用户:木讷大叔爱运维 背景 在蓝鲸社区“社区问答”帖子中发现这么一个需求: 究其原因,我在《不是CMDB筑高墙,运维需要一定的开发能力!》一文中已经介绍,在此我再简单重复下&#…

喜讯!全视通获评“医用气体行业优秀装备制造企业”称号

春意盎然,“渝”您相见。3月28日,“2024中国医学装备大会暨展览会”在重庆隆重启幕,本次展览会是我国医学装备领域政府部门、相关专家、头部企业深度参与的综合性展会,是先进技术最权威的展示平台之一,是引领创新发展的…

备战2024年中学生古诗文大会(初中组):单选题真题和独家解析

我们继续来做中学生古诗文大会(初中组)——简称初中生古诗文大会的一些真题,让大家了解初中生古诗文大会的考察内容和形式,并且提供了我独家的题目解析和答案,供初中的同学们学习和参考。 以下题目截取自我独家制作的在…

《基础设施即代码(IaC)》译者序

随着信息技术的飞速发展,我们对基础设施的理解也在不断深化。传统的基础设施往往被看作是硬件和软件的堆砌,而现在,基础设施的概念已经发生了巨大的变化。在当今这个信息化、数字化的时代,基础设施已经成为了企业和组织运行的核心…

Linux多进程通信(3)——详细说说共享内存原理及使用例程

1.共享内存原理及优缺点 共享内存的原理便是将相同的一片物理内存映射到进程A和进程B不同的逻辑地址空间,两个进程同时访问这块物理内存(共享内存)。 1)优点 共享内存是进程间通信访问速度最快。 例如消息队列,FIFO&…

喜尔康众多智慧酒店解决方案亮相上海酒店展,“十档妇洗”技术惊艳全馆

日前,2024上海国际酒店及商业空间博览会在上海新国际博览中心盛大举行,2000品牌同时亮相,引发行业广泛关注。 作为行业领先的智能家居企业,喜尔康30年来专注于为大家居行业提供智能化解决方案,此次参展展出了超静音智能…

ssm校园订餐系统

ssm校园订餐系统 一、项目简介 本项目是一套基于SSM实现的网上订餐系统 或 在线点餐系统 或 外卖点餐系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 详细介绍了网上订餐系统的实现,包括: 1.项目介绍 2.环境…

突破编程_前端_JS编程实例(分割窗体组件)

1 开发目标 分隔窗体组件旨在提供灵活的窗体分隔功能,支持横向分割与纵向分隔两种类型,并具备拖拽调整窗体比例的功能,同时提供最小比例设置,以防止窗体被过度缩小: 2 详细需求 2.1 分隔窗体类型 (1&…