vue设计原理-带你重走vue诞生路程

news/2025/2/22 5:21:18/

我们首先看下面这个小demo
在这里插入图片描述

demo源码:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>重走vue</title><style>div {width: 300px;padding: 15px;background-color: aqua;color: #000;margin: 50px auto;border: #13c159 double;border-radius: 20px;}div>p {padding: 5px;font-weight: 800;}</style>
</head><body><div><p id="name"></p><p id="age"></p><p id="appearance"></p></div><script>let student = {name: '林江涛',age: '刚满18岁(没多少年)',appearance: '江西吴彦祖'}// 显示姓名function showName() {document.querySelector('#name').textContent = `姓名: ${student.name}`}//显示年龄function showAge() {document.querySelector('#age').textContent = `年龄: ${student.age}`}//显示外貌function showAppearance() {document.querySelector('#appearance').textContent = `外貌: ${student.appearance}`}showName()showAge()showAppearance()</script>
</body></html>

从上面源码可以看出,这个demo设计为,展示一个对象student的数据,
在实际开发中,我们往往会需要改变student这个对象中的一些属性,然后在页面上展示
如下
在这里插入图片描述
我们可以看见,改变了student对象的name属性后,页面并没有跟着变化,
在没有vue的世界里自然是这样,

我们要想改动了student对象的name属性,就得再去调用对应的渲染函数
在这里插入图片描述

那为什么是调用showName()呢?为什么不是调用showAge()或showAppearance()?

你们看到这可能就得说,你这问的什么智障问题? showName()这个函数不就是用来展示student.name的值吗,

student.name的值改变了,要想页面跟着变,当然得重新调用 showName()

好的,如果你已经十分明白的知道了这个,那我们把上面的逻辑用人话整理一下

为什么student.name值改变了,要想使页面对应变化,需要调用showName()而不是其它函数?

是因为showName就是用来展示student.name这个属性的值得,showName在运行的过程中用到了student.name这个属性

换句更加简洁的话来说就是,showName()依赖于student.name这个属性,

showAge(),showAppearance()或其它函数,没有没有用到student.name这个属性,也就是说没有依赖于这个属性,所以从逻辑上来讲应该调用showName()这个函数,而不是其他函数

同理.,如果将来student.appearance这个属性变了,就应该去调用showAppearance()这个函数.因为showAppearance()依赖于student.appearance,
这样页面才会对应变化
在这里插入图片描述
那现在看起来一切和谐,没有什么问题,数据的改动,页面也能对应的改动

看起来没有问题,其实隐藏着一个巨大的问题,因为这个demo仅仅只有三个属性,依赖于它的函数也仅仅有三个,
但在实际的项目中,往往是拥有成千上万个对象,对象又可能有成千上万个属性,而依赖于此的对象更是数不胜数

在这种情况下,上面的写法,就完全不适用了你会发现,你改了一个属性,你完全不知道你应该去调用那些函数,或者这些函数根本就没写在同一个文档中,更甚者,你甚至不知道这个属性会在哪里被更改,比如直接控制台更改

这简直是灾难,

那能不能有一种方法,当我改动了一个属性后,会自动去调用依赖于它的方法?

这样的话,那既能避免手动调用有遗漏和不知道应该调用那个函数,又能保证页面和数据是统一的

那有目标了,就得想办法实现它

一个对象的属性,被改变了,我们能知道它被改变了,这时候就可以请出我们的defineProperty了
在这里插入图片描述
使用Object.defineProperty()修改一下student.name属性,这样每当student.name属性被获取时会调用对应的get函数
设置时会调用set函数
在这里插入图片描述

我们再对这两个函数进行一个小小的修改,加个变量存储student.name的值,这样赋值取值的时候就能有数据,
再分别给函数加个console.log()这样我们就能知道函数到底是否运行了

如下,打开控制台可以看到,函数是正常运行了
在这里插入图片描述

我们在对student.name进行修改,可以看到,现在程序自己知道我们读取了student.name的值或给它赋值了

在这里插入图片描述

知道了这个之后,那想让页面跟着变,自然就是调用对应的函数
如下
在这里插入图片描述

然后我们再修改student.name的值就能发现,页面也会跟着变化了

在这里插入图片描述
恭喜,如果你看到这,那我们已经完成阶段性胜利了,因为我们以及初步完成了数据的响应式,
即,当数据的值发生更改->对应页面也发生更改,

但是这样还不够,因为这代码写死了,意味着上面的数据变化,页面也对应变化的功能,仅仅只能针对student.name这一个属性,

	let internalVal = student.userObject.defineProperty(student, 'name', {get: () => {console.log('student.name被读取了')return internalVal},set: val => {internalVal = valconsole.log('student.name被赋值了赋值为' + val)showName()}})

那这是肯定不行的,因为我们的目的,是为了让所有的数据变化时,依赖于它的函数都能执行也就是页面都能做出响应的改变

所以我们就得想个办法,该如何让所有的数据改变时,依赖于他的方法都能执行?

既然Object.defineProperty这个方法是给某一个属性修改了,那么我们是不是就可以封装一个方法,然后在里面写一个循环,循环遍历传进来的对象给他每一个属性都修改成上面那种样子,不就能监控对象的所有属性了吗

示例如下

声明一个函数,这个函数接收一个对象,然后把对象遍历一遍,再使用defineProperty让对象的每一个属性都修改为拥有get和set方法的属性

	function observe(o) {for (let k in o) {let internalVal = o[k]Object.defineProperty(o, k, {get: () => {console.log(`对象的.${k}被读取了`)return internalVal},set: val => {internalVal = valconsole.log(`对象的.${k}被赋值了赋值为` + val)}})}}observe(student)

效果如下,
如图,可以看到修改或赋值student的所有属性

在这里插入图片描述

修改完之后,问题就来了,我该如何在set里面绑定调用依赖于对应属性的函数呢?

因为我们在set方法里压根就不清楚会调用那些函数,

这个时候我们就得回过头想一想,为什么要调用这些函数?因为这些函数依赖于student1,对象的属性
那也就是说,student[key]的操作,也就是说,会调用get这个方法,

那就代表着,可以让get收集访问过这个属性的函数名称,然后在set里面就可以使用循环依次调用了

但是这样还不够,这样只能知道到底get被调用了,不知道到底是谁调用了,

那怎么办?在家一层代理,这样询问代理就能知道具体是那个函数访问了属性

代码如下

	let distribute;let internalVal = student.userfunction observe(o) {for (let k in o) {let internalVal = o[k]let fnArr = []Object.defineProperty(o, k, {get: () => {if (distribute) {fnArr.push(distribute)}console.log(`对象的.${k}被读取了`)return internalVal},set: val => {internalVal = valconsole.log(`对象的.${k}被赋值了赋值为` + val)for (let i = 0; i < fnArr.length; i++) {fnArr[i]()}}})}}observe(student)function distributeFun(fn) {distribute = fn;fn()distribute = null}distributeFun(showName)distributeFun(showAge)distributeFun(showAppearance)student.name = '吴彦祖'student.age = 100

效果如下,

在这里插入图片描述

嗯,还有一些小bug比如再次调用age依赖的函数依然会加进去,

这里做一个去重就好
在这里插入图片描述

呐,大功告成


http://www.ppmy.cn/news/1213139.html

相关文章

关于道一云-七巧使用感悟

低代码&#xff0c;开发快速&#xff0c;缺点组件比较少但能满足大多数场景&#xff0c;如果需要的话得自己造&#xff0c;官方的技术群里面普遍问题响应就很快&#xff0c;技术文档这个会抽风&#xff0c;有些代码就改个筛选条件本地死活运行不了&#xff0c;去官网那里问他们…

简述数据存储的大小端序

例如一个整数, 6, 它的十六进制表示为: 0x00000006 共四字节. 位高位xx低位值0x000x000x000x06 例如我的存储缓冲区有四个字节: 地址0123值0000 如果我将这个数字存入时, 低位字节在低位地址, 则是小端序. 即, 地址0123值0x060x000x000x00 如果低位字节存储在高位地址, …

【代码随想录】算法训练计划18

1、513. 找树左下角的值 题目&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 思路&#xff1a; 递归&#xff0c;规则&#xff0c;基本可以自己写出来 var maxDepth int var res int fun…

MPC-模型预测控制笔记

线性mpc 凸优化 二次优化问题 1&#xff1a;建立预测模型 2&#xff1a;问题模型 3&#xff1a;求解优化问题 4&#xff1a;得到的优化控制驱动系统 上述方法与qp解一样 硬约束 硬约束 四组约束条件 二次规划求解 matlab代码&#xff1a; 软约束 可以用指数函数 加入…

Spring学习笔记——AOP(4)

Spring学习笔记——AOP&#xff08;4&#xff09; 一、学习AOP1.1 AOP的概述1.2 AOP思想实现方案1.3、模拟AOP的基础代码1.4、AOP的相关概念 二、基于xml配置AOP2.1 AOP基础入门2.2、XML方式AOP配置详解2.3、XML方式AOP原理剖析 三、注解式开发AOP3.1 注解式开发AOP入门3.2 AOP…

elastic-job 完结篇

一 elastic-job 1.1 案例场景分析 1.设置4个分片&#xff0c;10秒执行一次。 分片弹性扩容缩容机制测试&#xff1a; 测试1&#xff1a;测试窗口1不关闭&#xff0c;再次运行main方法查看控制台日志&#xff0c;注意修改application.properties中的 server.port&#xf…

【论文精读】DMVSNet

今天读的是一篇发表在ICCV 2023上的文章&#xff0c;作者来自华中科技大学。 文章地址&#xff1a;点击前往 项目地址&#xff1a;Github 文章目录 Abstract1 Introduction2 Relative Work3 Motivation3.1 Estimated bias and interpolated bias3.2 One-sided V.S. Saddle-shap…

使用MVS-GaN HEMT紧凑模型促进基于GaN的射频和高电压电路设计

标题&#xff1a;Facilitation of GaN-Based RF- and HV-Circuit Designs Using MVS-GaN HEMT Compact Model 来源&#xff1a;IEEE TRANSACTIONS ON ELECTRON DEVICES&#xff08;19年&#xff09; 摘要—本文阐述了基于物理的紧凑器件模型在研究器件行为细微差异对电路和系统…