vue2数据响应式原理(7) 收集依赖,用get和set叙述出最基础的至高vue哲学

news/2024/5/19 12:32:13/文章来源:https://blog.csdn.net/weixin_45966674/article/details/130393783

收集依赖在整个数据响应式中算是比较难的
首先 要理解这里所指的依赖
依赖 可能vue项目做多了就会想到 npm i 但其实跟这个是没有什么关系的

我们这里所指的依赖 是用到数据的地方
什么地方用到数据 什么地方就是依赖 简单说 就是依赖这个响应式数据

首先 我们看一下 vue1 和 vue2的区别

vue1的话 是 细粒度的依赖 他是 你在dom中 {{ 响应式数据 }} 这样去使用了 他就叫依赖

vue2的话 做了一定的优化 他 中等粒度的依赖 就是 看组件有没有用
在这里插入图片描述
而有一句非常 哲学 的话 在get中收集依赖 在set中触发依赖

简单说 用到这个数据的地方 就会触发get 谁触发get 谁就是依赖
而在set中 触发依赖 去更新依赖的数据

然后呢 关于实现原理 我这里就不直接讲了 开始我看到那个人都麻了 非常的蒙
我打算用我慢慢理解这个东西的顺序讲述出来 帮助大家更好的理解

先打开我们一直在写的案例 然后 在src里面创建一个
Dep.js
先创建一个类的结构

export default class Dep {constructor(value) {}
};

然后 再在src下创建一个 Watcher.js
也是写一个类结构

export default class Watcher {constructor(value) {}
}

然后 我们找到dataResp.js

现在最上面引入一下Dep 这个类

import Dep from './Dep.js';

然后在Observer类中这样改一下

class Observer{constructor(value) {this.dep = new Dep();//相当于  给拿到的对象  其中的__ob__绑定 值为thsi,在类中用this 表示取实例本身给__ob__赋值  最后一个enumerable为false 表示属性不参与for遍历def(value,'__ob__',this,false);if(Array.isArray(value)){Object.setPrototypeOf(value, arrayMethods);this.observeArray(value);}else{this.walk(value);}}walk(value) {for(let key in value){defineReactive(value,key);}}observeArray(arr) {for(let i = 0;i < arr.length;i++) {observe(arr[i]);}}
}

就是 先简答通过new 存了一下 Dep类对象 这里就涉及一个概念

每一个Observer的实例中都有一个Dep实例

而 这里 我们知道 每一个响应式的对象中 都会有一个 ob 而他存的就是 他走到Observer中new的实例对象

除此之外 还有 两个地方需要dep 还在 dataResp 文件下 我们的defineReactive修改函数体如下

const defineReactive = function(data,key,val) {const dep = new Dep();if(arguments.length == 2){val = data[key];}let subset = observe(val);Object.defineProperty(data,key,{enumerable: true,configurable: true,get() {console.log(`您正在获取${key}的值`);return val},set(value) {console.log(`您正在修改${key}的值,更改后的值为${value}`);if(value == val) {return}val = value;subset = observe(value);dep.notify();}});
}

我们这里 new得到一个Dep类对象 然后 在set时调用类中的notify

还有就是 既然响应式数据需要监听 我们的数组自然也需要
找到Arrays.js 改写代码如下

import { def } from './def.js';const arrayPrototype = Array.prototype;export const arrayMethods = Object.create(arrayPrototype);const redefineArrayMethod = ['push','pop','shift','unshift','splice','sort','reverse'
]redefineArrayMethod.forEach(item =>{const backupFunction = arrayPrototype[item];def(arrayMethods,item,function(){const result = backupFunction.apply(this, arguments);const ob = this.__ob__;const args = [...arguments];let inserted = [];switch (item) {case "push":case "unshift":inserted = args;break;case "splice":inserted = args.slice(2);break;}if(inserted){ob.observeArray(inserted);}console.log('数组执行了',item,'操作,值被修改为',this);ob.dep.notify();return result;},false);
})

因为我们对象上面是有 ob 这个属性的 我们直接通过const ob = this.ob;获取到 这就是个Observer对象 然后我们直接通过它去用dep下面的notify就好了

那么 首先 Dep 我们现在还是一个空的类 你这样直接调他里面的notify肯定报错 我们来写一下Dep.js

var uid = 0;
export default class Dep {constructor(value) {this.subs = [];this.id = uid++;}addSub(sub) {this.subs.push(sub);}depend(){if (Dep.target) {this.addSub(Dep.target);}}notify() {const subs = this.subs.slice();for (let i = 0;i < subs.length; i++) {subs[i].update();}}
};

首先 这个uid的作用在于每个id用于区分 第一个自然是 1 然后没实例一个 就会加一

然后 subs 则是用来存放订阅的数据的

然后 相对 我们的depend 也比较需要理解 这一块 先判断 拿不拿到当前的目标 Dep.target 这就是收集依赖的方法 谁触发了 get谁就是依赖 我们就需要通过Dep.target拿到当前这个目标节点 然后 通过addSub将这个依赖加到订阅数组里

那么 get收集依赖 我们自然就要改一下dataResp.js下defineReactive下get的代码了

const defineReactive = function(data,key,val) {const dep = new Dep();if(arguments.length == 2){val = data[key];}let subset = observe(val);Object.defineProperty(data,key,{enumerable: true,configurable: true,get() {if (Dep.target) {Dep.depend();if(subset) {subset.dep.depend();}}console.log(`您正在获取${key}的值`);return val},set(value) {console.log(`您正在修改${key}的值,更改后的值为${value}`);if(value == val) {return}val = value;subset = observe(value);dep.notify();}});
}

其实也就是在get中 判断当前有没有Dep.target这个目标节点 如果有就调一下depend存一下当前依赖订阅
然后判断 子集subset有没有 有的话 就也一起调用了

然后 我们写一下Watcher.js

import Dep from './Dep.js';
var uid = 0;export default class Watcher {constructor(target, expression, callback) {this.id = uid++;this.target = target;this.getter = parsePath(expression);this.callback = callback;this.value = this.get();}update() {this.run();}get() {var value;Dep.target = this;const obj = this.target;try {value = this.getter(obj);} finally {Dep.target = null;}return value;}run() {this.getAndInvoke(this.callback);}getAndInvoke(cb) {const value = this.get();if (value !== this.value || typeof value == "object") {const oldValue = this.value;this.value = value;cb.cal1(this.target, value, oldValue);}}
}
function parsePath(str) {var segments = str.split( ' .' );return (obj) => {for(let i = 0 ; i < segments.length; i++) {if (!obj) return;obj = obj[segments[i]]}return obj;}
}

最后 我们来实例化监听一个依赖实例收集
来到src下的 output.js 编写代码如下

import { observe } from "./dataResp"
import Watcher from "./Watcher"
const output = () => {var obj = {data: {data: {map: {dom: {isgin: true}},arg: 13},name: "小猫猫"},bool: [1,2,3,4]};observe(obj);new Watcher(obj, "data.data.arg",(res) =>{console.log("arg的值被改为了"+res);})obj.data.data.arg = 24;document.getElementById("text").innerHTML = obj.data.name;
}export default output

这里 我们new了一个Watcher类实例 他弟第一个参数 要监听那个对象 我们传了obj 第二个 要监听哪一个具体字段 这里 我们指向了 data.data.arg
这里 大家可以仔细看一下Watcher中的parsePath函数 做的正式 一层一层去把他找到的一个事情 然后 第三个参数 当数据改变触发依赖时 要做的事情

这里 我们简单输出了一下 arg的值被改为了 加他的新的值
监听完 我们也是马上写了 obj.data.data.arg = 24; 去修改监听的值来触发依赖
我们运行代码
在这里插入图片描述

然后 我们再改成这样

import { observe } from "./dataResp"
import Watcher from "./Watcher"
const output = () => {var obj = {data: {data: {map: {dom: {isgin: true}},arg: 13},name: "小猫猫"},bool: [1,2,3,4]};observe(obj);new Watcher(obj, "data.data.arg",(res) =>{console.log("arg的值被改为了"+res);})new Watcher(obj, "data.name",(res) =>{console.log("name的值被改为了"+res);})obj.data.data.arg = 24;obj.data.name = "大猫猫";obj.data.data.arg = 24;document.getElementById("text").innerHTML = obj.data.name;
}export default output

同一时间多监听一个 并来回调一次
在这里插入图片描述

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

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

相关文章

PWM 呼吸灯实验

PWM 呼吸灯实验 FPGA实现一个PWM模块&#xff08;硬件&#xff09;来控制灯的亮灭。 实验原理 PWM本质上就是一个输出脉冲的硬件&#xff0c;通过改变一个周期高电平&#xff08;占空比&#xff09;的时间来对其他的硬件进行控制&#xff0c;比如电机。 呼吸灯的实现利用了人…

ChatGPT实现语义分析情感分类

语义分析情感分类 我们从开源社区找到了中科院谭松波博士整理的携程网酒店评论数据集(https://raw.githubusercontent.com/SophonPlus/ChineseNlpCorpus/master/datasets/ChnSentiCorp_htl_all/ChnSentiCorp_htl_all.csv)。一共七千余条数据&#xff0c;包括 label 和 review …

vue封装公共组件库并发布到npm库详细教程

vue组件封装的原理&#xff1a;利用vue框架提供的api: Vue.use( plugin )&#xff0c;我们需要把封装好组件的项目打包成vue库&#xff0c;并提供install方法&#xff0c;然后发布到npm中。Vue.use( plugin )的时候会自动执行插件中的install方法。 一、组件库代码目录 目录…

【Python_Opencv图像处理框架】边缘检测、轮廓检测、图像金字塔

写在前面 本篇文章是opencv学习的第四篇文章&#xff0c;主要讲解了边缘及轮廓检测的主要操作&#xff0c;并对两种图像金字塔简单的介绍了一下&#xff0c;作为初学者&#xff0c;我尽己所能&#xff0c;但仍会存在疏漏的地方&#xff0c;希望各位看官不吝指正&#x1f60d; …

C语言 sizeof, size_t, strlen

C语言 sizeof, size_t, strlen 文章目录 C语言 sizeof, size_t, strlen一. sizeof1.1 返回结构体长度 二. size_t三. sizeof 和 strlen 一. sizeof 返回一个结构体或者类型所占的内存字节数 1.1 返回结构体长度 这里我编写了2个结构体&#xff0c;区别在于数组问题 #include …

基于本地知识构建简易的chatPDF

Langchain chatglm-6b 文章目录 Langchain chatglm-6b前言一、实验记录1.1 环境配置1.2 代码理解1.3 补充内容 二、总结 前言 介绍&#xff1a;一种利用 ChatGLM-6B langchain 实现的基于本地知识的 ChatGLM 应用 Github: https://github.com/imClumsyPanda/langchain-Chat…

《个人博客部署上线教程一》Halo搭建个人博客网站

Halo搭建个人博客网站 一、docker部署Halo 目前测试了两种方法安装Halo&#xff0c;第一种是使用Jar包安装:提供JAR包资源&#xff0c;不过因为使用jar包部署需要Java11才可以&#xff0c;我本机使用的是Java8&#xff0c;所以暂时不做调整。第二种是通过docker安装。 1.1 启…

if条件语句

if条件语句 条件测试 test 测试表达式是否成立&#xff0c;若成立返回0&#xff0c;否则返回其他数值 格式1 &#xff1a;test 条件表达式&#xff1b;格式2 &#xff1a;[ 条件表达式 ] echo $?参数作用-d测试是否为目录 (Directory)-e测试目录或文件是否存在(Exist)-f测…

【java笔记】java多线程

目录 一、概念 1.1 什么是进程&#xff1f; 1.2 什么是线程&#xff1f; 1.3 什么事多线程&#xff1f; 1.4 进程和线程的关系 二、线程对象的生命周期 三、实现线程有两种方式 3.1 继承 java.lang.Thread&#xff0c;重写 run方法 3.2 实现 java.lang.Runnable 接口…

八、vue_options之computed、watch属性选项

一、computed计算属性使用 &#xff08;1&#xff09;复杂data的处理方式 &#xff08;2&#xff09;computed 计算属性 computed计算属性初体验&#xff1a; 在我们通过Vue调用createApp方法传入一个对象的时候&#xff0c;我们之前写了data属性、methods属性&#xff0c;这…

HTB-Time

HTB-Time 信息收集80端口 立足pericles -> root 信息收集 80端口 有两个功能&#xff0c;一个是美化JSON数据。 一个是验证JSON&#xff0c;并且输入{“abc”:“abc”}之类的会出现报错。 Validation failed: Unhandled Java exception: com.fasterxml.jackson.core.JsonPa…

低代码是开发的未来,还是只能解决边角问题的鸡肋?

随着互联网行业寒冬期的到来&#xff0c;降本增效、开源节流几乎成为了全球互联网厂商共同的应对措施&#xff0c;甚至高薪酬程序员的“35岁危机”一下子似乎变成了现实。程序员的高薪吸引了各行各业的“跨界选手”&#xff0c;是编程门槛降低了吗&#xff1f;不全是&#xff0…

Linux Ansible管理变量、管理事实、管理机密

目录 Ansible变量 变量定义范围 变量类型 定义变量并引用 事实变量与魔法变量 事实变量 魔法变量 Ansible加密 ansible-vault参数 ansible-vault举例 Ansible变量 Ansible支持利用变量来存储值&#xff0c;并且可以在Ansible项目的所有文件中重复使用这些值 变量可能…

Hadoop3.2.4+Hive3.1.2+sqoop1.4.7安装部署

目录 一、软件包 二、JDK部署 1.JDK解压 2.设置环境变量 3.环境验证 4.分发JDK相关文件至Node_02、Node_03 5.环境生效 三、Zookeeper部署 1.Zookeeper解压 2.Zookeeper配置 3.创建myid文件 4.设置环境变量并添加映射 5.分发ZooKeeper 相关文件至Node_02、Node_0…

Qt — Graphics/View框架

文章目录 前言一、Qt图形系统介绍二、Graphics/View框架 前言 Qt的Graphics/View框架被用来存放、显示二维图形元素&#xff0c;处理那些对图形元素进行操作的交互命令。 一、Qt图形系统介绍 Qt 应用程序的图形界面包含各种控件&#xff0c;比如窗口、按钮、滚动条等。所有这…

【单目标优化算法】沙猫群优化算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

4月24日~4月26日学习总结

一&#xff0c;刷题目情况&#xff0c;已经完成了8道题目&#xff0c;对于其中一些题目做一下题解。 这个题目的意思是找到的两个位置l和r&#xff0c;为了做到这个数组的l到r的子数组经过排序后&#xff0c;会变成输入的另外一个数组&#xff0c;这个题目的思路就是首先找到在…

服务(第十篇)Nginx和tomcat反向代理(动静分离)

正向代理&#xff1a; 当用户想访问某一网址时&#xff0c;用户先访问代理服务器&#xff0c;然后由代理服务器向目标网址发送请求最终将数据返回代理服务器&#xff0c;最后代理服务器将数据返回给用户这一过程我们称之为正向代理。 反向代理&#xff1a;基本流程是与正向代理…

(04)基础强化:接口,类型转换cast/convert,异常处理,传参params/ref/out,判断同一对象

一、复习 1、New的截断是指什么&#xff1f; new除了新开空间创建初始化对象外&#xff0c;还有一个隐藏父类同名方法的作用。 当子类想要隐藏父类同名的方法时用new&#xff0c;用了new后父类同名方法将到此为止&#xff0c;后面 继承的…

centos7部署FastDFS服务

一、安装需要的相关依赖 yum -y install make cmake gcc gcc-c 因为我的服务器已经安装了gcc&#xff0c;所以略去 使用gcc -v查看版本 yum -y install zip unzip 安装性能事件通知库 yum -y install libevent 安装nginx依赖 yum -y install libevent yum -y install zli…