vue3解读—reactivity响应式实现

2023-11-15

前言:Vue3 中引入了proxy进行数据劫持,而effect是响应式系统的核心,而响应式系统又是 vue3 中的核心,所以vue3的解读要从 effect 开始讲起。

1.reactivity和effect的使用

目前vue3的各个模块都可以单独安装,首先我们需要安装npm i @vue/reactivity,从中引入我们需要的方法,下面是我简单写一段测试代码。

const { reactive, effect } = require("@vue/reactivity");
let a = reactive({
    value: 1,
});
let b;
effect(() => {
    b = a.value + 10;
    console.log(b);
});
a.value = 10; 

先别急着看我的输出,想想自己心中的答案是什么?

下面公布控制台的实际输出:

image.png

总结:我们会发现effect函数执行了两次,一次是我们往effect函数传入匿名函数时,它立即执行了一次;接下来是我们对响应式变量a.value进行重新赋值时(或者说effect内函数所依赖的响应式变量发生变化时)它又执行了一次。

接下来我们就好好思考下,如何设计一个reactive方法,将传入的值变成响应式,以及实现一个effect函数,其内函数所依赖的响应式数据变化时,该函数会再次执行。

2.Dep和effect的实现

reactive会将传入的变量变成响应式数据,包括对象等数据;地基需要从下往上一层一层的搭建,我们这里先实现将单一变量编译成响应式数据,并用effct对其监听。废话不多说,上代码:

第一步:

class Dep {
    constructor(val) {
        this._val = val;
    }
    get value() {
        return this._val;
    }
    set value(newVal) {
        this._val = newVal
    }
}
const dep = new Dep(10);
console.log('取值:', dep.value);
dep.value = 20;
console.log('赋值:', dep.value); 

我们通过class类去创建一个简单的变量,用其构造器constructor初始化对象属性,使用settergetter存取器拦截该属性的存取行为。此时dep就是响应式数据吗?想啥呢!但我们会很熟悉后面的取存值行为,这不就是我们在vue3中使用ref声明变量时,取值和赋值的写法吗!

image.png

有了这一步的铺垫,我们接下来的思路是不是更清晰了,只需要在初始化一个Dep时,收集依赖该dep的函数,并在dep值发生变化时,再次执行这个依赖函数,是不是就能实现前面reactiveeffect所达到的效果。ready go

第二步:

let currentEffect;
class Dep {
    constructor(val) {
        this.effects = new Set(); // 储存依赖当前变量的函数,并去重
        this._val = val;
    }
    get value() {
        this.depend();
        return this._val;
    }
    set value(newVal) {
        this._val = newVal
        this.notice(); // 赋值时触发依赖
    }
    // 收集依赖
    depend() {
        if(currentEffect) { // 要记得判空
            this.effects.add(currentEffect);
        }
    }
    // 触发依赖
    notice() {
        this.effects.forEach(effect => {
            effect();
        });
    }
}
const effectWatch = (effect) => {
    currentEffect = effect;
    effect(); // 别忘了首先就会执行一次
    currentEffect = null;
}

const dep = new Dep(10);
let b;
effectWatch(() => {
     b = dep.value + 10;
     console.log('effectWatch', b);
});
dep.value = 20; 

为了讲解时方便区分,所以我在实现effect时,重新取名叫effectWatch;不墨迹了,赶紧看代码,相对于第一步,Dep类新增了两个方法,分别是储存依赖的函数和触发依赖的函数。首先effectWatch在调用时就会执行一次依赖函数,并且是在effectWatch的参数中取dep.value值时,就会将当前依赖函数储存,当我们对dep.value赋值时,会再次触发依赖的函数。

到此为止我们只是实现了一个简化单一的‘reactive’effect,真正的reacive可不仅仅只是传一个普通字符的功能,一起想想下一步该怎么做呢?

3.reactive实现

前面我们已经实现了简单地Dep,它只是一个单一的变量,远远不能满足我们的开发需求。如果我们通过嵌套调用Dep,是不是就能实现reactive的功能了?而且开局我们已经了解到需要使用到proxy对数据进行劫持,那么是不是就好实现多了。

第一步

const reactive = (raw) => {
    return new Proxy(raw, {
        get(target, key) {
            console.log('get----', target[key]);
            return Reflect.get(target, key);
        },
        set(target, key, value) {
            console.log('set----', key, value);
            return Reflect.set(target, key, value);
        }
    });
}
const user = reactive({
    name: '春赏百花冬观雪',
});
user.name;
user.age = 24;
user.age; 

我们先科普下proxy,就作者而言,很早就学习过该方法,但对于它的应用确实少的可怜(唯唯诺诺的小菜鸡)。proxy就是在我们访问对象前添加了一层拦截,从而实现基本操作的拦截和自定义(我理解为过滤),而且proxy常常与Reflect成对出现,Reflect也就是反射,它的出现简化了我们调用_Object对象_的代码,保持JS的简单,它们之间的基情还需要读者自行去了解哦。

对于user我们可以理解为一个较为复杂的对象,此时的它的每一个属性key所对应的值是不是相当于我前面的dep呢,那么我们只需要再访问该key值时将它通过Dep初始化,从而使整个user的任意属性值发生变化,那么所依赖的函数也能再次执行了,整个user也就成了响应式对象了。从代码来看,第一步依旧是 利用存取器对该user对象进行监听。

image.png

第二步

const targetMap = new Map(); // 用于搜集经过reactive初始化的变量
const getDep = (target, key) => {
    let depsMap = targetMap.get(target); // 从targetMap取,如果有的话
    if (!depsMap) { // 没有就先储存
        depsMap = new Map();
        targetMap.set(target, depsMap);
    }
    let dep = depsMap.get(key); // 并将dep与target的key建立连接
    if (!dep) {
        dep = new Dep();
        depsMap.set(key, dep);
    }
    return dep;
};
const reactive = (raw) => {
    return new Proxy(raw, {
        get(target, key) {
            const dep = getDep(target, key);
            dep.depend(); // 当我们访问对象每个属性时,都会收集依赖
            return Reflect.get(target, key);
        },
        set(target, key, value) {
            const dep = getDep(target, key);
            const result = Reflect.set(target, key, value); // 重新设置值之后
            dep.notice(); // 触发依赖
            return result; // 再return
        }
    });
};
const user = reactive({
    name: '春赏百花冬观雪',
});
effectWatch(() => {
    user.name;
    console.log('effect---', user.name);
});
user.name = '晓看天色暮观云'; 

这里要注意为什么接收Reflect.set后再return抛出,因为我们需要将user.name的值更新后,紧接着触发我们收集到的依赖,最后才能抛出以完成setimage.png

作者也是前端小白一枚,只是通过自己的学习并记录以方便自己的回顾,也希望读者大佬们能提出宝贵意见,共同进步。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue3解读—reactivity响应式实现 的相关文章

  • 我如何在 AngularJS 中监听点击并按住的情况?

    我制作了一个时间计数器 您可以通过单击按钮来增加或减少时间 然而 我希望当我单击并按住按钮时 时间的价值会不断攀升 所以目前如果你看到我的Plunkr http plnkr co edit BxX9x5zYFMXVqt5JsN1F p pr
  • chrome 调试器承诺在暂停时不会解析?

    也许我没有正确调试承诺 但基本上 如果您在断点处停止并运行异步代码 它实际上不会完成 直到您恢复执行为止 这是一个问题 调试器允许您快速试验多个 api 方法 但如果您恢复它 您就不能 debugger now type the follo
  • 如何通过单击图像预览上的“x”从文件输入中删除图像?

    我目前有一个文件输入 一旦用户上传图像 就会显示图像预览 在图像预览上 有一个 x 可以从列表中删除图像预览 单击此 x 后 有什么方法可以从输入中的文件集中删除图像吗
  • 如何将udp发送到udp node.js服务器?

    我对此很陌生 所以我真的不知道我在做什么 但我已经设置了一个 node js udp 服务器 我想从客户端 来自网站 向它发送一个数据包 但我不知道如何在 javascript 中做到这一点 或者是否可能 我不是在研究如何从 Node js
  • 动态速度计 javascript 或 jquery 插件

    我希望有动态ajax插件在页面上显示速度计 一个想法是我设置一个背景并旋转针 有人知道相关插件吗 这里有一些供您参考 http bernii github com gauge js http bernii github com gauge
  • 网站 YouTube 嵌入视频不断播放

    我正在使用 youtube 提供的 iframe 在我的网站上嵌入视频 我还使用了一个 css 弹出窗口 这是我从这个页面学到的http www pat burt com web development how to do a css po
  • 如何在ASP.NET Webform中使用Jquery表单插件?

    我遇到了这个插件 http malsup com jquery form getting started http malsup com jquery form getting started 我想知道如何在 ASP NET WebForm
  • jquery 验证错误位置

    这看起来很简单 但我无法弄清楚 我正在使用 jquery 验证插件 我验证所有文件 但我想要的是在输入文本行中显示验证消息警报 例如在电子邮件输入中 请填写电子邮件地址 但现在它出现在所有字段下 在我的html中
  • 使用 moment.js 检查输入日期是否为星期一

    好吧 我想检查日期是否是星期一 例如 var myDate new Date moment myDate DD MM YYYY dayIs monday 在我的国家 一周的第一天是星期一 所以 我真的想检查输入日期是否是一周的开始 我尝试使
  • 动画进度元素值

    我有一个progress元素 该元素如下所示 div class container div div div
  • JavaScript 中数组的 HTML 数据列表值

    我有一个简单的程序 它必须从服务器上的文本文件中获取值 然后将数据列表填充为输入文本字段中的选择 为此 我想要采取的第一步是我想知道如何动态地将 JavaScript 数组用作数据列表选项 我的代码是
  • 如何在 Angular 中从父组件访问子组件?

    I have mat paginator在子组件a中 如下所示 子组件 html
  • IE11不监听MSFullscreenChange事件

    我正在尝试使用 Bigscreen js 在 IE11 中使用全屏 但 IE11 不监听 MS FullscreenChange 事件 document addEventListener MSFullscreenChange functio
  • 将数组排序为第一个最小值、第一个最大值、第二个最小值、第二个最大值等

    编写一个JS程序 返回一个数组 其中第一个元素是第一个最小值 第二个元素是第一个最大值 依此类推 该程序包含一个函数 该函数接受一个参数 一个数组 该函数根据要求返回数组 输入示例 array 2 4 7 1 3 8 9 预期输出 1 9
  • 单击关闭按钮后不显示 Google 一键登录 UI

    我正在尝试按照本指南使新的谷歌一键登录工作 https developers google com identity one tap web https developers google com identity one tap web
  • Chartjs刻度标签位置

    尝试让 Y 轴刻度标签看起来像image https i stack imgur com XgoxX png 位于秤顶部且不旋转 缩放选项当前如下所示 scales yAxes id temp scaleLabel display true
  • 为什么“tbody”不设置表格的背景颜色?

    我在用 tbody 作为 CSS 选择器来设置background color在一个表中 我这样做是因为我有多个 tbody 表内的部分 它们具有不同的背景颜色 我的问题是 当使用border radius在细胞上 细胞不尊重backgro
  • 当用户单击链接时,如何记录 MixPanel 事件?

    当用户单击某种类型的链接时 我试图在 MixPanel 中记录一个事件 我正在使用 JQuery 不引人注意地完成此操作 据我所知 我需要添加一个回调函数 以便在记录事件后将用户带到 URL 这是我正在使用的代码 不幸的是
  • 数据表日期范围过滤器

    如何添加日期范围过滤器 like From To 我开始进行常规搜索和分页等工作 但我不知道如何制作日期范围过滤器 我正在使用数据表 1 10 11 版本 My code var oTable function callFilesTable
  • 如何在 javascript 正则表达式中匹配平衡分隔符?

    我原以为这个问题是不可能的 据我所知 Javascript 的正则表达式既没有递归插值 也没有漂亮的 NET 平衡组功能 但问题就在那里 如问题 12 所示正则表达式 alf nu http regex alf nu 匹配平衡对 lt an

随机推荐

  • css中div和spand,layout - DIV's vs. Tables or CSS vs. Being Stupid - Stack Overflow

    CSS may not be a religion but it is how browsers interpret HTML for layout Like it or not all modern browsers use some v
  • Qt帮助系统的制作

    原文链接Qt帮助系统的制作 Qt的帮助系统很好看 观察自带的帮助都是HTML格式 当然最好的方法是使用QtWeb模块 但好多Qt版本不支持啊 想到QTextBrowser可以显示图片啥的 一阵乱撸终于搞定 分享之 系列目录 本文是系列教程
  • 程序大佬用的电脑配置终于曝光了,难怪

    作为一名程序员 选一台适合自己的笔记本电脑至关重要 一款优秀的笔记本电脑可以提高工作效率 减轻工作压力 但是市面上的笔记本电脑种类繁多 不知道该如何选择 在本文中 我们将为大家推荐10款适合程序员使用的笔记本电脑 并附带京东的购买链接和价格
  • Sklearn fit , transform ,fit_transform

    一 关于sklearn fit 和transform sklearn里的封装好的各种算法使用前都要fit fit之后 可以调用各种API方法 transform是其中一个API fit原义指的是安装 使适合的意思 有点train的含义 但是
  • Java通过正则表达式获取域名

    由于 url getHost 获取域名会有漏洞 会获取不完整 因此通过正则表达式获取域名 上代码 String url http www linuxidc com entry 4545 0 Pattern p Pattern compile
  • Nginx 第三方健康检测模块的使用

    ngx http upstream check module 模块 模块开源地址 https github com yaoweibin nginx upstream check module 官网介绍 http tengine taobao
  • linux 网络

    网络基础 协议的概念 什么是协议 从应用的角度出发 协议可理解为 规则 是数据传输和数据的解释的规则 假设 A B双方欲传输文件 规定 第一次 传输文件名 接收方接收到文件名 应答OK给传输方 第二次 发送文件的尺寸 接收方接收到该数据再次
  • tf好朋友之matplotlib的使用——subplot分格显示

    tf好朋友之matplotlib的使用 subplot分格显示 分格显示的方法 利用plt subplot2grid进行分格显示 利用gridspec GridSpec进行分格显示 应用示例 在学习matlab的时候 图像是可以分格显示的
  • 1032. 挖掘机技术哪家强(20)

    1032 挖掘机技术哪家强 20 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN Yue 为了用事实说明挖掘机技术到底哪家强 PAT组织了一场挖掘机技能大赛 现请你
  • maven仓库中_remote.repositories的作用

    首先直接给结论 remote repositories的作用是当maven本地仓库缓存了jar pom的情况下修改了maven的配置文件 settings xml 后依然会去远程仓库获取 以org slf4j slf4j api 1 5 6
  • 科创板、香港主板、纳斯达克三地部门上市条件和要求

    转自 https zhuanlan zhihu com p 69144513 科创板 香港主板 纳斯达克三地部门上市条件和要求 发布于 2019 06 14
  • 一、创建型模式:工厂方法模式(Factory Method)

    请MM去麦当劳吃汉堡 不同的MM有不同的口味 要每个都记住是一件烦人的事情 我一般采用Factory Method模式 带着MM到服务员那儿 说 要一个汉堡 具体要什么样的汉堡呢 让MM直接跟服务员说就行了 定义 核心工厂类不再负责所有产品
  • MySQL-多表关联

    多表关联 多张数据表之间是可以有一定的关联关系 这种关联关系可以通过外键约束实现 多表的分类 一对一 一对多 多对多 一对一 一张表对应另一张表 适用场景 人和身份证 一个人只能有一个身份证 一个身份证只能对应一个人 建表原则 在任意一个表
  • python爬虫实战练手——————淘宝网站的爬取

    python爬虫是很好的数据分析手段 可以进行爬虫程序来进行爬取网站 下面是淘宝的爬取 淘宝搜索书包 然后得到以下的界面 注意到下面的分页 可以通过进行分页的改变来进行多页数据的爬取 爬取多页 这里用到了和重要的re库 也就是正则表达式库
  • Windows10下Linux子系统Ubuntu使用教程(8)——升级WSL2,及解决遇到的问题

    WSL 2 是 WSL 中体系结构的新版本 它更改 Linux 发行版与 Windows 交互的方式 WSL 2 的主要目标是提高文件系统性能并增加系统调用的完全兼容性 每个 Linux 发行版都可以作为 WSL 1 或 WSL 2 发行版
  • vue 获取服务端base64位图片之后的处理

    目录 Base64是什么 Base64可以在Url中传输吗 Base64是加密算法么 Base64的应用场景有哪些 Base64的优点 Base64的缺点 关于vue中img无法展示base64位图片的原因分析 Base64是什么 Base
  • CSS-选择器的基本用法

    目录 一 CSS的分类 1 行内样式 2 内部样式 3 外部样式 二 选择器是什么 三 选择器具体种类 1 类选择器 2 标签选择器 3 ID选择器 4 通配符选择器 一 CSS的分类 1 行内样式 通过 style 属性 来指定某个标签的
  • Java实现Token的生成与验证

    二 基于JWT的token认证实现 JWT JSON Web Token 其实token就是一段字符串 由三部分组成 Header Payload Signature 1 引入依赖
  • 爬虫之简单js逆向

    本次js逆向没有存在代码混淆 所以还是比较简单的 重要的就是js逆向的思路 目标网站https notice qb com detail noticeId 215让我们开始吧 进入网站后按F12 查看DOC中的 可以看出该网页一部分内容是异
  • vue3解读—reactivity响应式实现

    前言 Vue3 中引入了proxy进行数据劫持 而effect是响应式系统的核心 而响应式系统又是 vue3 中的核心 所以vue3的解读要从 effect 开始讲起 1 reactivity和effect的使用 目前vue3的各个模块都可