Vue源码分析2--Ojbect的变化侦测

2024-05-13 06:38

本文主要是介绍Vue源码分析2--Ojbect的变化侦测,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先Vue的最大特点是数据驱动视图。什么是数据驱动试图呢?我可以用一条公式来描述
UI= reader(state)
在这条公式中,UI是页面输出,state是状态/数据,render就是vue,vue当检测到数据变化,就会触发改变UI。

那么问题来了,Vue怎么根据数据变化而更改视图呢?下面我们总结一下变化侦测。并且从零基础开始手写Vue的变化侦测

什么是变化侦测

变化侦测是数据变化了,就要更新视图。
目前的前端三大框架中均有涉及。在Angular中是通过脏值检查流程来实现变化侦测;在React是通过对比虚拟DOM来实现变化侦测,而在Vue中也有自己的一套变化侦测实现机制。

这里我们分两篇文章分别描述vue变化侦测,分别是Object类测试和Array类侦测。

Object的变化侦测

首先我们先了解JS语言一个Object.defineProperty 方法,该方法会直接在一个对象上定义一个新属性,或者修改一个对象现有的属性,并且返回此对象。
这个方法就是Vue变化侦测的核心,我们在这里可以成为上帝的钥匙
在这里我们可以看defineProperty几个重要的属性和方法

label类别备注默认值
configurable属性当属性该属性为true时,该属性的描述符能够被改变,同事该属性可以能从对象上删除false
enumerable属性当该属性为true时,该属性可以出现在对象的枚举属性中false
writable属性当该属性为true时,可以任意更改属性的值false
get方法属性的 getter 函数,当访问属性的时候,就会调用此函数,一般我们成为“getter”undefined
set方法属性的 setter函数,当修改属性的时候,就会调用此函数,一般我们成为“setter”undefined

Object数据变得可观性

数据客观性是随时可以知道数据什么时候被读写。当属性被读和写的时候,会触发 get 和 set 属性。
请看以下例子

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title></title><script src="./index.js" type="text/javascript" charset="utf-8"></script></head><body></body><script type="text/javascript">let student ={name :"Tony"}let val ="GoGO"Object.defineProperty(student,"name",{enumerable: true,configurable: true,get(){console.log('name 属性被读取了');return val},set(newVal){console.log("name 属性被修改:%s",newVal);val = newVal;// return val}})console.log(student.name)student.name ="Janny"</script>
</html>

在网页控制台显示

name 属性被读取了
index.html:28 GoGO
index.html:23 name 属性被修改:Janny

上面demo说明每次读数据时候会调用get,修改属性时候调用set

有了以上基础,我们开始写Object数据变化侦测。

首先我们先看一个流程图,我的学习经验是先把框架以流程图形式记录下来,方便理解。

在这里插入图片描述

下面开始手把手从零基础写Vue变化侦测。

定义一个Observer class类

请看代码

index.js


function defineReactive(obj,key,val){console.log("我是defineReactive",obj,key);if(arguments.length == 2){val = obj[key];}Object.defineProperty(obj,key,{enumerable: true,configurable: true,get(){console.log('%s 属性被读取了',key);return val;},set(newVal){console.log('%s 属性被写入 %d',key,newVal);if(val === newVal){return ;}val = newVal;}})
}

index.html

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title></title><script src="./index.js" type="text/javascript" charset="utf-8"></script></head><body></body><script type="text/javascript">let student ={name :"Tony"}let val ="GoGO"defineReactive(student,"name");student.name=1000;console.log(student.name)</script>
</html>

执行以上代码结果是

我是defineReactive {name: "Tony"} name
index.js:15 name 属性被写入 1000
index.js:11 name 属性被读取了
index.html:17 1000

这里我们把Object.defineProperty封装在defineReactive函数里面。

定义一个Observer 类

这里我们定义一个Observer 类,该类是功能是给每个object添加 __ob__ 属性。

什么是 __ob__ 属性?
value.__ob__={value,enumerable,writable:true,configurable:true
}

意思是给每个object的 value修改属性,让其可以读写删除,可以枚举,赋予初值。

所以我们在 index.js定义一个def函数

function def(obj,key,value,enumerable){Object.defineProperty(obj,key,{value,enumerable,writable:true,configurable:true})
}

流程图,分别写Observer 类,obverse函数,请看以下demo

function def(obj, key, value, enumerable) {//第一步Object.defineProperty(obj, key, {value,enumerable,writable: true,configurable: true})
}
class Observer {//第二步constructor(value) {console.log('我是Observer constructor', value);def(value, '__ob__', this, false)//添加__ob__属性this.walk(value);}walk(value){//读取object里面每个属性for(let k in value){defineReactive(value,k);}}
}function observe(value) {//第三步console.log("准备调用observe", value)if (typeof value !== 'object')return;console.log("调用observe 啦", value);var ob;if (typeof value.__ob__ !== 'undefined') {ob = value.__ob__;} else {ob = new Observer(value);}}function defineReactive(obj, key, val) {console.log("我是defineReactive", obj, key);if (arguments.length == 2) {val = obj[key];}Object.defineProperty(obj, key, {enumerable: true,configurable: true,get(){console.log('%s 属性被读取了', key);return val;},set(newVal) {console.log('%s 属性被写入', key, newVal);if (val === newVal) {return;}val = newVal;}})
};
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title></title><script src="./index.js" type="text/javascript" charset="utf-8"></script></head><body></body><script type="text/javascript">var Student ={name:"Tony",year:20,grade:{math:100,chinese:45}}// defineReactive(Student,"name");observe(Student);console.log(Student);Student.name ="Janny";console.log(Student.name);Student.grade.math =20;</script>
</html>

在Observer类中,在 constructor 中添加 __ob__ 属性,然后在每个属性调用defineReactive方法,通过Object.defineProperty修改对象每个属性的值。此时可以监听到getter 和setter。

在 observe 方法中,把value传输Observer类,Observer类作用是使Object第一个层级属性改为可响应式。
于是以上程序运行结果是:
在这里插入图片描述

在这里插入图片描述
我们展开Student对象,可以发现,在Ojbect第一层存在 __ob__ 属性,但是在grade对象中,发现没有 __ob__ 属性。说明我们必须递归添加 __ob__ 属性。

问题来了,怎么在每一层 Object 条件 添加 __ob__ 属性呢?

在流程图上,我们把 defineReactive 的 val 再次指向observe,所以我们添加ChildOB,形成闭环。所以我们修改defineReactive 方法如下:

function defineReactive(obj, key, val) {console.log("我是defineReactive",obj,key);if (arguments.length == 2) {val = obj[key];}var childOb = observe(val);//递归Object.defineProperty(obj, key, {enumerable: true,configurable: true,get() {console.log('%s 属性被读取了',key);return val;},set(newVal) {console.log('%s 属性被写入了 %d:',key,newVal);if(val === newVal){return ;}val = newVal;childOb = observe(newVal);//添加新属性时要observe一下}})
};

上面是修改后得代码,我们添加两行,但是会有小伙伴会问,这个流程图是一直闭环,那会不会一直无限循环下去?终点在哪里?
在observe方法里面,判断了当输入的值不是object类型,就退出该方法。

if (typeof value !== 'object')return;

另外还有一个小细节,其实这里有一个闭包
function defineReactive(obj, key, val) 第三个参数val,其实是个闭包。

查看执行结果,我们发现Object对象每一层都存在一个 __ob__ 属性
在这里插入图片描述
然后每次修改Student里面属性值,都会调用get和set属性,此时说明编写数据可观性成功。

		Student.name ="Janny";console.log(Student.name);Student.grade.math =20;

在这里插入图片描述
总结:
1.学习Object对象数据客观性要先了解整体流程图
2.然后要熟悉 Object.defineProperty 方法,知道get和set属性。
3.善用chrome浏览器console控制台每个数据进行浏览分析,加深理解。

要查看源码,请看此链接

这篇关于Vue源码分析2--Ojbect的变化侦测的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/984956

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者