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响应式实现 的相关文章

  • Angular UI 模式的范围问题

    我无法理解 使用角度 UI 模式的范围 虽然这里不是很明显 但我已经正确设置了模块和所有内容 据我所知 但这些代码示例尤其是我发现错误的地方 index html 其中重要部分 div class btn group div
  • 可能未处理的承诺拒绝(id 0)类型错误 GET 或 HEAD 请求不允许主体

    import React from react import FlatList ActivityIndicator Text View from react native export default class FetchExample
  • FullCalendar-vue + Typescript:属性“getApi”不存在

    我尝试将 FullCalendar vue 与 Typescript 结合使用 但在访问其 API 时遇到错误 我的日历设置如下
  • Jquery 悬停卡

    我在用着http designwithpc com Plugins Hovercard http designwithpc com Plugins Hovercard 但我不知道如何在悬停卡上声明 var 每个工作描述都有自己的 ID 当悬
  • 显示具有多个父代的 D3 树

    我目前有this http bl ocks org mbostock 4339083图已实现 我希望在描述具有多个父节点的子节点时保持结构和可折叠性 有没有办法做到这一点 我研究了力图 但我也想保留一组层次结构 这意味着 1 级的父级可以有
  • 位置特征检测:固定

    我正在尝试找到一个脚本来检测设备是否放置position fixed元素相对于视口而不是整个文档 目前 标准桌面浏览器和 Mobile Safari 适用于 iOS 5 都是这样做的 而 Android 设备则相对于整个文档放置固定元素 我
  • .push() 将多个对象放入 JavaScript 数组中返回“未定义”

    当我将项目添加到beats数组然后console log用户时 我得到了数组中正确的项目数 但是当我检查 length 时 我总是得到 1 尝试调用索引总是会给我 未定义 如下所示 Tom beats 1 我想我错过了一些明显的东西 但这让
  • 如何使用有角度的材料创建卡片网格?

    我正在尝试使用 ng repeat 创建每行三张卡片的网格 我有一个普通的 javascript 对象数组附加到范围 下面的代码将为每张卡创建一个新行 div div
  • 如何在React Native Android中获取响应头?

    您好 我想在获取 POST 请求后获取响应标头 我尝试调试看看里面有什么response with console log response 我可以从以下位置获取响应机构responseData但我不知道如何获取标题 我想同时获得标题和正文
  • 导航栏下拉菜单(折叠)在 Bootstrap 5 中不起作用

    我在尝试使用以下命令创建响应式菜单或下拉按钮时遇到问题Bootstrap 5一切似乎都正常 导航图标和下拉图标出现 但它不起作用 当我单击nav图标或dropdown按钮 无dropdown menu apears 我想特别提到的是 我还包
  • JavaScript 继承;调用和原型

    要在Javascript中实现继承 通常需要执行以下两个步骤 假设我有一个基类 Animal var Animal function name this name name 我现在想从中派生一个子类 Dog 所以我想说 var Dog fu
  • 改变 JavaScript 中的顶部填充

    以下是我在 css 中设置顶部填充的方法 body font size font size px margin 0 padding 100px 0 20px 0 width 100 important 如何使用最简单的 javascript
  • 使用 Google 日历源时如何禁用 FullCalendar 中的活动链接?

    我正在使用 FullCalendar 库从 Google 日历加载日历中的事件 不幸的是 事件添加到日历后 它们是可点击的 当您点击该活动时 您会自动重定向到 Google 日历页面以查看该特定活动 或者如果您有足够的访问权限 则可以直接对
  • LeafleteachLayer函数不会迭代所有Layer

    使用 GeoJSON 数据数组创建一些标记 getJSON GetLocationsServlet function data L geoJSON data onEachFeature onEachFeature addTo mymap G
  • 如何从浏览器向服务器发送“页面将关闭”消息?

    我想向每个 html 文档添加一个脚本 JavaScript 该脚本向服务器发送两条消息 页面确实打开了 页面将关闭 此消息包含页面打开的时间 打开消息应在文档加载时 或加载完成时 发送 这是简单的部分 The close message
  • Highcharts jQuery 渲染问题 - 所有浏览器

    我在尝试使用构建堆积柱形图时遇到了一个奇怪的问题高图表 http www highcharts com 当图表呈现时 在您调整浏览器大小之前 不会显示列无论如何 导致图表重绘 我认为 图表的其余部分显示 轴 标题等 但不显示列本身 我在 I
  • react-native - 图像需要来自 JSON 的本地路径

    你好社区 我正在react native中开发一个测试应用程序 并尝试从本地存储位置获取图像 我实际在做什么 我将图像直接链接源提供给 var 并在渲染函数中调用此方法 react 0 14 8 react native 0 23 1 np
  • 在 iOS 7 Safari 中,如何区分通过边缘滑动与后退/前进按钮的 popstate 事件?

    在 iOS 7 Safari 中 现在有两种后退 前进导航方式 使用底部的传统后退 前进按钮箭头或从屏幕边缘滑动 我正在使用动画在 ajax 应用程序中的页面之间进行转换 但如果用户通过边缘滑动进行导航 我不想触发该转换 因为这本身就是一个
  • 如何从 json 文件创建模型? (ExtJS)

    这是我想使用 json 文件创建的模型 Ext define Users extend Ext data Model fields name user id type int name user name type string 为了根据服
  • 将数组从 jquery ajax 传递到代码后面

    我必须将二维数组传递给在asp net网页代码后面编写的页面方法我有一个变量objList作为二维数组 我使用以下代码来实现此目的 但没有成功 并且未调用页面方法 脚本语言 function BindTable objList ajax u

随机推荐

  • 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的各个模块都可