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的代理对象proxyObjconst proxyObj = reactive(obj);//不访问obj,访问代理对象proxyObjconsole.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,太烧脑了

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

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

相关文章

【全栈小5】我的创作纪念日

目录 前言机缘收获粉丝和原创个人成就六边形战士 回顾文章原代码代码优化 憧憬 前言 全栈小5 ,有幸再次遇见你: 还记得 2019 年 03 月 29 日吗? 你撰写了第 1 篇技术博客: 《前端 - 仿动态效果 - 展开信息图标》 在这平凡的一天&…

【JS】null和undefined有什么区别

前言 JS的作者Brendan Eich曾说过两者的区别: null means “no object”, undefined > “no value”.Really it’s an abstraction leak:null and objects shared a Mocha type tag. 翻译后: null 表示“没有对象”,undefined…

STM32学习笔记(9_3)- USART串口代码

无人问津也好,技不如人也罢,都应静下心来,去做该做的事。 最近在学STM32,所以也开贴记录一下主要内容,省的过目即忘。视频教程为江科大(改名江协科技),网站jiangxiekeji.com 本期介…

Memcached 教程之Memcached介绍(一)

Memcached 教程 Memcached是一个自由开源的,高性能,分布式内存对象缓存系统。 Memcached是以LiveJournal旗下Danga Interactive公司的Brad Fitzpatric为首开发的一款软件。现在已成为mixi、hatena、Facebook、Vox、LiveJournal等众多服务中提高Web应用…

POSIX信号量

1.快速认识信号量接口 POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。我们之前认识SystemV信号量时有这样三个结论: 1.信号量的本质是一把计数器 2.申请信号量本质就是预…

进程调度算法

进程调度算法 进程调度算法先来先服务调度基于优先级调度(Priority Scheduling)短进程优先 / 最短剩余时间优先轮转法(Round-Robin Scheduling)高响应比优先调度算法(Highest Response Ratio Next)多级反馈…

jupyter 设置工作目录

本博客主要介绍: 如何为jupyter设置工作目录 1.打开 anaconda prompt , 执行 jupyter notebook --generate-config 执行这个命令后会生成一个配置文件 2. 打开jupyter_notebook_config.py文件编辑 搜索notebook_dir,把这行代码的注释取消,…

stm32再实现感应开关盖垃圾桶

一、项目需求 检测靠近时,垃圾桶自动开盖并伴随滴一声,2秒后关盖 发生震动时,垃圾桶自动开盖并伴随滴一声,2秒后关盖 按下按键时,垃圾桶自动开盖并伴随滴一声,2秒后关盖 硬件清单 SG90 舵机,…

HTTP 与 HTTPS 的区别

基本概念 HTTP(HyperText Transfer Protocol:超文本传输协议)是一种应用层协议,主要用于在网络上进行信息的传递,特别是用于Web浏览器和服务器之间的通信。 它使用明文方式发送数据,这意味着传输的内容可…

公司服务器被.rmallox攻击了如何挽救数据?

公司服务器被.rmallox攻击了如何挽救数据? .rmallox这种病毒与之前的勒索病毒变种有何不同?它有哪些新的特点或功能? .rmallox勒索病毒与之前的勒索病毒变种相比,具有一些新的特点和功能。这种病毒主要利用加密技术来威胁用户&am…

【JavaScript】数组 ③ ( JavaScript 数组长度 | 修改数组长度 | 数组案例 )

文章目录 一、JavaScript 数组长度1、数组长度2、修改数组长度 二、数组案例1、求数组元素平均值2、求数组元素最大值 一、JavaScript 数组长度 1、数组长度 在 JavaScript 中 , 数组长度 可以通过 数组变量的 length 属性 获取 , 该属性 返回 数组中的元素数量 , 也就是 数组长…

基于微信小程序的日语词汇学习设计与实现(论文+源码)_kaic

日语词汇学习小程序 摘 要 日语词汇学习小程序是高校人才培养计划的重要组成部分,是实现人才培养目标、培养学生科研能力与创新思维、检验学生综合素质与实践能力的重要手段与综合性实践教学环节。本学生所在学院多采用半手工管理日语词汇学习小程序的方式&#x…

如何用Python脚本自动发送邮件?

目录 1. 基础知识 1.1. SSH(Secure Shell)协议 1.2. SMTP(Simple Mail Transfer Protocol)协议 1.3. SSH协议与SMTP协议之间的关系 2. QQ邮箱设置 2.1. 开启SMTP服务 2.2. 编写脚本 3. 测试成功 1. 基础知识 邮件的发送过…

从TCP/IP协议到socket编程详解

​ 我的所有学习笔记:https://github.com/Dusongg/StudyNotes⭐⭐⭐ ​ 文章目录 1 网络基础知识1.1 查看网络信息1.2 认识端口号1.3 UDP1.4 TCP1.4.1 确认应答机制1.4.2 TCP三次握手/四次挥手为什么是三次握手为什么是四次挥手listen 的第二个参数 backlog—— 全…

01_安装VMwareWorkstation虚拟机

环境:Win10 19045 软件版本:VMware-workstation-17.5.1 一、下载链接 Download VMware Workstation Pro 二、安装(无脑下一步) 安装位置自选,最好非系统盘。 增强型键盘驱动自选。 更新自选。 快捷方式自选。 三、…

fastllm在CPU上推理ChatGLM2-6b,就来看这篇文章,速度很快

介绍: GitHub - ztxz16/fastllm: 纯c++的全平台llm加速库,支持python调用,chatglm-6B级模型单卡可达10000+token / s,支持glm, llama, moss基座,手机端流畅运行纯c++的全平台llm加速库,支持python调用,chatglm-6B级模型单卡可达10000+token / s,支持glm, llama, moss基…

Linux系统----------探索mysql数据库MHA高可用

目录 一、MHA概述 1.1 什么是 MHA 1.2MHA 的组成 1.2.1MHA Node(数据节点) 1.2.2MHA Manager(管理节点) 1.3MHA 的特点 1.4MHA工作原理 1.5数据同步的方式 1.5.1同步复制 1.5.2异步复制 1.5.3半同步复制 二、搭建 MySQ…

小赢科技公布2023年业绩:业绩稳健增长,服务“触角”有效延伸

近日,金融科技公司小赢科技(NYSE:XYF)发布了2023年第四季度及全年未经审计的财务业绩。 财报显示,小赢科技2023年全年总净营收约为48.15亿元,同比增长35.1%;净利润约为11.87亿元,同比增长46.2%…

平价的挂耳式耳机有哪些?五大高口碑品牌,深度测评严选!

随着技术的发展,市面上的一些高端开放式耳机已经在音质上有了显著的提升,甚至可以媲美一些入耳式耳机。与传统入耳式耳机相比,开放式耳机不会对耳道造成压迫,这减少了耳朵的疲劳感,使得长时间聆听音乐变得更加舒适。由…

FastAPI+React全栈开发08 安装MongoDB

Chapter02 Setting Up the Document Store with MongoDB 08 Installing MongoDB and friends FastAPIReact全栈开发08 安装MongoDB The MongoDB ecosystem is composed of different pieces of software, and I remember that when I was starting to play with it, there w…