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

news2024/11/17 13:28:12

我们首先看下面这个小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.user
	Object.defineProperty(student, 'name', {
	  get: () => {
	    console.log('student.name被读取了')
	    return internalVal
	  },
	  set: val => {
	    internalVal = val
	    console.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 = val
	        console.log(`对象的.${k}被赋值了赋值为` + val)
	      }
	    })
	  }
	}
	observe(student)

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

在这里插入图片描述

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

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

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

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

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

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

代码如下

	let distribute;
	let internalVal = student.user
	function 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 = val
	        console.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.coloradmin.cn/o/1200602.html

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

相关文章

Python开发者的利器:掌握多种执行JS的方法

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com JavaScript&#xff08;JS&#xff09;是一种常用的脚本语言&#xff0c;通常用于网页开发&#xff0c;但有时也需要在Python中执行或调用JavaScript代码。这种需求可能是因为希望与网页进行交互&#xff0c;或者…

机器学习数据预处理——Word2Vec的使用

引言&#xff1a; Word2Vec 是一种强大的词向量表示方法&#xff0c;通常通过训练神经网络来学习词汇中的词语嵌入。它可以捕捉词语之间的语义关系&#xff0c;对于许多自然语言处理任务&#xff0c;包括情感分析&#xff0c;都表现出色。 代码&#xff1a; 重点代码&#…

【数据结构】树与二叉树(十二):二叉树的递归创建(算法CBT)

文章目录 5.2.1 二叉树二叉树性质引理5.1&#xff1a;二叉树中层数为i的结点至多有 2 i 2^i 2i个&#xff0c;其中 i ≥ 0 i \geq 0 i≥0。引理5.2&#xff1a;高度为k的二叉树中至多有 2 k 1 − 1 2^{k1}-1 2k1−1个结点&#xff0c;其中 k ≥ 0 k \geq 0 k≥0。引理5.3&…

【Redis系列】Redis的核心命令(上)

哈喽&#xff0c;大家好&#xff0c;我是小浪。那么上篇博客教会了大家如何在Linux上安装Redis&#xff0c;那么本篇博客就要正式开始学习Redis啦&#xff0c;跟着俺的随笔往下看~ 1、启动Redis 那么如何启动Redis呢&#xff1f;最常用的是以下这个命令&#xff1a; redis-cl…

“艾迪-东软杯”第六届武汉理工大学新生程序设计竞赛

A.Capoos Acronym Zero 题目描述 yz 和他的朋友 ea 和 zech 一起养了一群 Capoo。 这些 Capoo 非常聪明&#xff0c;但不知道为什么&#xff0c;它们并没有从三人那里学到怎么写算法题&#xff0c;而是出于某种原因开始研究语言学&#xff0c;并发明了一套自己的暗语。这门暗语…

设计模式之十一:代理模式

代理可以控制和管理访问。 RMI提供了客户辅助对象和服务辅助对象&#xff0c;为客户辅助对象创建和服务对象相同的方法。RMI的好处在于你不必亲自写任何网络或I/O代码。客户程序调用远程方法就和运行在客户自己本地JVM对对象进行正常方法调用一样。 步骤一&#xff1a;制作远程…

【C++初阶】类与对象(三)

目录 一、再谈构造函数1.1 初始化列表1.1.1 初始化列表写法1.1.2 哪些成员要使用初始化列表 1.2 初始化列表的特点1.2.1 队列类问题解决1.2.2 声明顺序是初始化列表的顺序 1.3 explicit关键字1.3.1 explicit关键字的作用 二、static成员2.1 类的静态成员概念2.2 类里创建了多少…

C++ 模板保姆级详解——template<class T>(什么是模板?模板分哪几类?模板如何应用?)

目录 一、前言 二、 什么是C模板 &#x1f4a6;泛型编程的思想 &#x1f4a6;C模板的分类 三、函数模板 &#x1f4a6;函数模板概念 &#x1f4a6;函数模板格式 &#x1f4a6;函数模板的原理 &#x1f4a6;函数模板的实例化 &#x1f34e;隐式实例化 &#x1f349;显式实…

Halcon WPF 开发学习笔记(4):Halcon 锚点坐标打印

文章目录 专栏前言锚点二次开发添加回调函数辅助Model类 下集预告 专栏 Halcon开发 博客专栏 WPF/HALCON机器视觉合集 前言 Halcon控件C#开发是我们必须掌握的&#xff0c;因为只是单纯的引用脚本灵活性过低&#xff0c;我们要拥有Halcon辅助开发的能力 锚点开发是我们常用的…

记录一次某某虚拟机的逆向

导语 学了一段时间的XPosed&#xff0c;发现XPosed真的好强&#xff0c;只要技术强&#xff0c;什么操作都能实现... 这次主要记录一下我对这款应用的逆向思路 apk检查 使用MT管理器检查apk的加壳情况 发现是某数字的免费版本 直接使用frida-dexdump 脱下来后备用 应用分…

【ATTCK】MITRE Caldera - 测试数据泄露技巧

CALDERA是一个由python语言编写的红蓝对抗工具&#xff08;攻击模拟工具&#xff09;。它是MITRE公司发起的一个研究项目&#xff0c;该工具的攻击流程是建立在ATT&CK攻击行为模型和知识库之上的&#xff0c;能够较真实地APT攻击行为模式。 通过CALDERA工具&#xff0c;安全…

Git版本控制系统之分支与标签(版本)

目录 一、Git分支&#xff08;Branch&#xff09; 1.1 分支作用 1.2 四种分支管理策略 1.3 使用案例 1.3.1 指令 1.3.2 结合应用场景使用 二、Git标签&#xff08;Tag&#xff09; 2.1 标签作用 2.2 标签规范 2.3 使用案例 2.3.1 指令 2.3.2 使用示例 一、Git分支&…

55基于matlab的1.高斯噪声2.瑞利噪声3.伽马噪声4.均匀分布噪声5.脉冲(椒盐)噪声

基于matlab的1.高斯噪声2.瑞利噪声3.伽马噪声4.均匀分布噪声5.脉冲&#xff08;椒盐&#xff09;噪声五组噪声模型&#xff0c;程序已调通&#xff0c;可直接运行。 55高斯噪声、瑞利噪声 (xiaohongshu.com)

“第六十六天”

这个我记得是有更优解的&#xff0c;不过还是明天发吧&#xff0c;明天想一想&#xff0c;看看能不能想起来 #include<string.h> int main() {char a[201] { 0 };char b[201] { 0 };scanf("%s %s", a, b);int na strlen(a);int nb strlen(b);int i 0, j …

基于SpringBoot+Vue的在线学习平台系统

基于SpringBootVue的在线学习平台系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 用户界面 登录界面 管理员界面 摘要 本文设计并实现了一套基于Spri…

C语言——贪吃蛇

一. 游戏效果 贪吃蛇 二. 游戏背景 贪吃蛇是久负盛名的游戏&#xff0c;它也和俄罗斯⽅块&#xff0c;扫雷等游戏位列经典游戏的⾏列。 贪吃蛇起源于1977年的投币式墙壁游戏《Blockade》&#xff0c;后移植到各种平台上。具体如下&#xff1a; 起源。1977年&#xff0c;投币式…

Leetcode421. 数组中两个数的最大异或值

Every day a Leetcode 题目来源&#xff1a;421. 数组中两个数的最大异或值 解法1&#xff1a;贪心 位运算 初始化答案 ans 0。从最高位 high_bit 开始枚举 i&#xff0c;也就是 max⁡(nums) 的二进制长度减一。设 newAns ans 2i&#xff0c;看能否从数组 nums 中选两个…

数据结构—二叉树的模拟实现(c语言)

目录 一.前言 二.模拟实现链式结构的二叉树 2.1二叉树的底层结构 2.2通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树 2.3二叉树的销毁 2.4二叉树查找值为x的节点 2.5二叉树节点个数 2.6二叉树叶子节点个数 2.7二叉树第k层节点个数 三.二叉树的遍历 3.1…

基于SpringMVC模式的电器网上订购系统的设计

大家好我是玥沐春风&#xff0c;今天分享一个基于SpringMVC模式的电器网上订购系统的设计&#xff0c;项目源码以及部署相关请联系我&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 本系统利用现在比较广泛的JSP结合后台SpringMybatisAjax编写程序的方式实现的。 在…