vue2数据响应式原理(4) 递归侦测对象所有属性,解密vue响应式对象__ob__是干什么的

news2024/11/17 1:47:58

我们上文写的这个方法 并不能很好的侦测对象所有的属性 或者说 不能比较简介的侦测所有属性
在这里插入图片描述
在实际业务中 对象里面套对象 也不是什么很少见的事 例如这样
在这里插入图片描述
这种 我们用上一种方法 就很麻烦了 所以 我们需要了解新的方法

要完成完整的属性监听 我们就需要一个工具类 这个类的名字是自己取的 这里建议叫 Observer 字面意思就可以看出 这个类起到一个观察的作用

而这个工具类的作用 就是 将一个object对象中的 每个属性 以及每个层级中的属性 都变成一个侦测的响应式数据

我们打开之前写的项目 将 dataResp.js 代码修改如下

const defineReactive = function(data,key,val) {
    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);
        }
    });
}

const def = function(obj,key,value,enumerable) {
    Object.defineProperty(obj,key,{
        value,
        enumerable,
        //true  设为可写
        writable: true,
        //true  设为可被删除
        configurable: true
    });
}

class Observer{
    constructor(value) {
        //相当于  给拿到的对象  其中的__ob__绑定 值为thsi,在类中用this 表示取实例本身给__ob__赋值  最后一个enumerable为false 表示属性不参与for遍历
        def(value,'__ob__',this,false);
        this.walk(value);
    }
    walk(value) {
        for(let key in value){
            defineReactive(value,key);
        }
    }
}

export const observe = function(value) {
    if(typeof value != "object") return;
    var ob;
    if (typeof value.__ob__ !== 'undefined'){
        ob = value.__ob__ ;
    }else{
        ob = new Observer(value);
    }
    return ob;
}

被改的非常的复杂 不过不要急 看我效果 我们慢慢讲解
将output.js代码更改如下

import { observe } from "./dataResp"
const output = () => {
    var obj = {
        data: {
            data: {
                map: {
                    dom: {
                        isgin: true
                    }
                },
                arg: 13
            },
            name: "小猫猫"
        },
        bool: {}
    };
    observe(obj);
    console.log(obj);
    obj.data.data.map.dom.isgin = false;
    document.getElementById("text").innerHTML = obj.data.name;
}

export default output

我们看看运行效果
首先 是看我们这个obj对象 被console.log输出在控制台 我们发现 他的每一次都被加上了一个 ob 的东西
很多人就会说 啊 我在vue响应式数据中也会看到这个 没错 这就是vue参照的一个实现方式
在这里插入图片描述
然后我们看运行效果
在这里插入图片描述
很显然 系统监听到了我们对obj.data.name的访问 所以 连着上面的data也被输出了一下 直到name 这一级结束 这就说明 你操作了一个数据 他的父节点也会被触发 比较 最为一个对象 自己的结构 也是他们内容的一部分 子集被访问或变量 也算是他们被访问或修改

然后很清晰的看到 系统捕获到了我们对obj.data.data.map.dom.isgin的修改

说明 这个方法已经实现了全部内容的监听 那么 我们来讲一下工作原理
在这里插入图片描述
这个图感兴趣的可以看一下 但老实说 我猜大部分一眼上去是看不懂的 我们从代码上将吧

首先 我们定义了一个 obj 对象 他的内部结构比较复杂 层级比较多 然后引用了dataResp.js 中的observe函数 参数就是我们的obj
然后进到observe中 我们先判断传进来的参数是不是一个对象
如果是对象 就不受影响 继续往下 否则 直接结束语句
在这里插入图片描述
这里 值得一提的是 因为判断语句是typeof 所以这个判断 数组和对象都是可以进来的 要的也就是这种效果

在这里存一个 ob 变量 用来存返回值

然后我们会判断 当前这个属性下面的 __ob__属性 是不是未定义
在这里插入图片描述
如果定义了 那么 他就不会等于undefined 就直接让ob等于他的__ob__然后返回就好了 这里 就解密 了 确实 __ob__没什么高大上的 就是一个标记 如果__ob__不是未定义 表示 当前的这个对象已经被处理过了 不用继续了 避免递归死循环
如果没有达到条件 说明 元素 __ob__是未定义状态 就需要处理 我们就new Observer 类 将他传进类里面去
然后我们再来看Observer
在这里插入图片描述
这里 constructor拿到的这个参数value就是我们new Observer时传进来的对象
然后 我们调用了 def方法 我们来看 def

第一个参数 value 他用在了Object.defineProperty第一个参数 说明 第一个参数是要声明响应式的对象 而这里 我们Observer里传的value 就是我们扔给Observer的对象 而第二个 也作为了Object.defineProperty的第二个参数 要给对象的哪个字段绑定 这里 我们就传了字符串 ob 说明 我们要绑定的是当前对象下的__ob__字段 Object.defineProperty的特性包括 如果对象没有要声明的字段 他会帮你创建 所以 我们没有__ob__他也会帮你创建出来
然后第三个参数 这个字段的值 就是 value对象的__ob__字段的值 我们Observer给了个 this 我们都知道 在类中使用 this 就是拿到当前类的实例 所以 我们将类new出来的实例 赋值给了__ob__
最后一个参数是enumerable 这个我们之前看过 就是 如果这个你设true 你用for遍历这个对象时 就可以看到这个字段 反之 设false 就不会参与for遍历 没人会喜欢这个 __ob__参与遍历吧 里面的信息又没用 他只是个标记
在这里插入图片描述
然后回到Observer的下半段代码
在这里插入图片描述
调用了声明在类中的walk方法 传入的参数还是value 当前的实例对象
walk 拿到对象的第一件事就是将他遍历开
而 我们目前这个value是之前传到observe 中的 obj 对象 遍历他 就会遍历出他下面的data和bool
在这里插入图片描述
然后 for遍历出啦的 对应的是键 而key是键 value 还是obj对象 所以
它遍历两次 传给defineReactive的参数分别是
obj对象, 字符串类型的 “data”
obj对象, 字符串类型的 “bool”

然后 我们来看defineReactive函数
在这里插入图片描述
进来先判断了 参数是不是只有两个 因为我知道我只传了两个参数啊
所以判断 如果只有两个参数 说明第三个参数 val是没有值的 所以
val = data[key]
根据上面的参数 我们知道 两次遍历调用 这句话分别代表
obj[“data”]
obj[“bool”]
就会取到当前传进来的对象的值
然后继续
在这里插入图片描述
我们让后面的对象继续去调用最开始obj 调用的 observe
obj在这个过程中给 data和bool声明了响应式 那么 继续回去调用这个方法的就还是data和bool
bool因为是个空的 所以 到Observer 的walk方法中 因为没有子集遍历 而停止
而data会继续带出下面data和name继续参与这个过程
在这里插入图片描述
data中的data自然没得说 继续重复过程
但 name 因为不是对象或数字 到observe刚开始 就会被判断拦住 并停止
在这里插入图片描述
继续看会到obj的这一层
在这里插入图片描述
因为 这里是通过遍历对象obj获取的key 所以 声明的分别是 obj的两个子集的响应式 第一个参数 要声明响应式的对象 obj 要声明的字段 就是两次遍历出来的key了
最后 在set中的subset = observe(value); 是保证当改变后 继续保存新值的响应

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

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

相关文章

js+css实现简单的弹框动画

效果图 只是一个简单的演示demo,但是可以后面可以优化样式啥的 刚开始元素的display为none,然后,为了给元素展示时添加一个动画,首先要添加样式类名show,让它覆盖display:none,变得可见。然后,添…

Day951.认知负载 -遗留系统现代化实战

认知负载 Hi,我是阿昌,今天学习记录的是关于认知负载的内容。 一、怎样理解认知负载? 作为开发人员,不管是不是工作在遗留系统上,一定面临过来自业务方或项目经理的灵魂拷问: 为什么这个需求这么简单&…

车载网络 - Autosar网络管理 - 跳转状态

四、Autosar网络管理跳转状态 网络模式对应报文状态 Autosar网络管理报文各个状态对应的网络管理报文和应用报文的发送和接收状态。 网络模式 网络管理报文 应用报文 收发类型 发送报文 接收报文 发送报文 接收报文 总线睡眠模式(BSM) No Yes No NA 准备总线睡眠模…

探索Whisper语音识别

问题一:python多版本切换 背景:有了anaconda环境 还有一个c盘的不知道什么东西 我准备下载一个python3.9.9 去官网 然后安装,安装之前一定要把原来的python卸载干净。 3.9.9安装不上,我用3.10 切换的话,就是去环境…

总结828

学习目标: 4月(复习完高数18讲内容,背诵21篇短文,熟词僻义300词基础词) 学习内容: 暴力英语:回环诵读之前的文章,背150个单词,背《冰与火之歌》-守夜人誓词 高等数学&…

《Effective C++》读书笔记(二):构造/析构/赋值运算(条款05~条款12)

目录 1. 条款05:了解C默默编写并调用哪些函数 2. 条款06:若不想使用编译器自动生成的函数,就该明确拒绝 3. 条款07:为多态基类virtual析构函数 4.条款08:别让异常逃离析构函数 5.条款09:绝不在构造和析…

Vue CLI 服务

使用命令 在一个 Vue CLI 项目中,vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。 这是你使用默认 preset 的项目的 package.json&…

2023红明谷杯部分WP

0x00 签到 一直点就能得到flag 0x01 Dreamer 拿到题感觉有点儿懵 先下发靶机看一眼 梦想家CMS,好嘛,我直接一手查找官网 直接一手演示中心碰运气 哎嘿嘿,运气不错进去了,突然想起之前有位大佬写的关于Dreamer CMS的代码审…

【Linux网络设置】

目录 一、查看网络接口信息1.1、查看所有活动的网络接口信息1.2、查看指定网络接口信息 二、查看主机名称2.1、hostname命令2.2、永久设置主机名 三、查看路由表条目route命令 四、查看网络连接情况4.1、netstat命令4.2、ss命令 五、测试网络连接ping命令 6、跟踪数据包tracerr…

CorelDRAW2023最新版本配置及新功能介绍

从简单的线框到令人称叹的水平,使用CorelDRAW Graphics Suite 2023开始您的设计之旅:一套完整的专业图形设计应用程序,用于矢量插图、布局、照片编辑等。CorelDRAW平面设计软件通常也被叫做CDR,CDR广泛应用于排版印刷、矢量图形编…

关于电脑出厂时间查询工具的构思

在做一个单位的计算机盘点、管理的时候,很容易遇见需要知道电脑的采购时间,或者出厂时间。这个信息能够帮助管理人员决定电脑是否该按定期报废制度进行报废或更换。 目前为止,作者接触过的各类电脑,没有看到过哪台电脑有专门的一…

windows系统中安装目标检测平台detectron2

更多内容,欢迎访问老五笔记 detectron2是Facebook研发并开源的目标检测平台,包含了大量业内最具代表性的目标检测、图像分割、关键点检测算法等。Detectron2基于新版的Pytorch进行更新,包含了更大的灵活性与扩展性。​ 笔者将在本文中介绍如…

IS220UCSAH1A利用电子和空穴两种载流子导电的,所以叫做双极型电路

IS220UCSAH1A利用电子和空穴两种载流子导电的,所以叫做双极型电路 美国的通用电气公司(General Electric Company,以下简称 GE)想要称霸整个工业互联网,但却失败了。为什么呢? 多年来,GE 一直在…

关于CSDN文章内嵌视频自动播放问题

关于CSDN文章内嵌视频自动播放问题 1. 源由2. 分析3. 反馈4. 沟通5. 总结6. 附录-Firefox配置7. 附录-Microsoft Edge配置 1. 源由 这个问题是4月初发现的,主要现象就是页面上的视频一起自动播放了。 鉴于笔者有不少帖子都是文字、表格、图片、视频结合的。视频是…

机器学习:opencv案例——人脸检测

目录标题 实验数据实验原理实验步骤实验结果 实验数据 lena.jpg face3.jpg video.mp4 实验原理 (1)图片灰度转换 OpenCV 中有数百种关于在不同色彩空间之间转换的方法。 当前, 在计算机视觉中有三种常用的色彩空间: 灰度、 BG…

redis lpush rpop List消息队列实现

List 队列: 生产者存入消息: LPUSH queue2 msg1 LPUSH queue2 msg2 LPUSH queue2 msg3 消费者消费消息: RPOP queue2 RPOP queue2 RPOP queue2写个死循环消费: while true://没消息阻塞等待,3秒超时返回null,设置0时没消息一直浪…

JavaSE学习进阶day07_02 异常

第三章 异常 3.1 异常概念 异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是: 异常 :指的是程序在执行过程中,出现的非正常的情况&#xff0…

Android---屏幕适配

为什么要适配 由于 Android 系统的开放性,任何用户、开发者、OEM 厂商、运营商都可以对 Android 进行定制,于是导致运行 Android 的设备多种多样,它们有着不同的屏幕尺寸和像素密度。尽管系统可以通过基本的缩放和调整大小功能使界面适应不同…

【LeetCode: 53. 最大子数组和 | 暴力递归=>记忆化搜索=>动态规划 | 分治法 】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

抽象类,内部类,匿名类

java学习第十天 抽象类 1.用abstract关键字来修饰一个类时,这个类就叫抽象类 访问修饰符 abstract 类名 { } 2.用abstract关键字来修饰一个方法时,这个方法就是抽象方法访问修饰符abstract返回类型方法名(参数列表);//没有方法体 3.抽象类的价值更多作用是在于设计,是设计者…