一步一步分析将数据响应式实现出来

news2024/11/24 23:32:53

写在前面

vue2 的数据响应式已经非常成熟且过时了,但是相信很多人还是对原理的东西一知半解,甚至还是不知道他究竟是怎么实现的,今天我们就试着一步一步分析看看响应式需要解决哪些问题,具体的问题难点是什么?

数据响应式

数据响应式就做了两件事,第一就是数据变化通知函数,第二就是函数进行视图也就是页面的变化 所以数据响应式就是数据变化引起视图更新

实现一个数据响应式需要具备的条件

  • 需要一个方法设置数据变化的时候映射到页面
  • 需要一个方法数据变化要可以及时调对应的方法

难点是什么?

  • 怎么知道数据变化了?
  • 数据变化之后怎么知道是哪个方法该更新?

实现第一件事

const data = { name: 'tom', age: 16 }

// TODO: 设置数据变化映射到页面
function setName() {
	const name = document.getElementById('name')
	name.innerHTML = data.name
}

function setAge() {
	const age = document.getElementById('age')
	age.innerHTML = data.age
}

实现第二件事

setName()
setAge()

// TODO: 两秒之后继续执行 测试页面更新的情况
setTimeout(() => {
	data.age = 22
	data.name = "JIM"
	setName()
	setAge()
}, 2000)

这两件事单独做都很简单,甚至没有任何的技术难度,那么问题就是我们怎么知道数据变化了?

解决怎么知道数据变化了的问题

Object.defineProperty
这玩意不就可以监测到吗?这个东西不仅仅可以给对象设置新的属性和设置属性的一些属性,同时他也可以知道对象的变化,变化之后调用一下方法不就好了吗?有方法了事情就好办了,直接开整

let __FV = data.name
Object.defineProperty(data, 'name', {
	get: function () {
		console.log('🚀 调用了 get()')
		return __FV
	},
	set: function (V) {
		console.log(`🚀 数据及时更新,更新的结果是:${V}`)
		__FV = V
		// TODO: 及时更新页面数据
		setName()
		setAge()
	}
})

和预期是一致的:
在这里插入图片描述

怎么知道是哪些方法需要调用呢?

现在是因为只有两个方法,所以是只需要分别调用一次就好了,但是如果方法很多的时候,或者是用户只更新了年龄,没有更新姓名,你再直接全部更新就不太好了,
我们现在怎么可以知道哪些属性对应的是哪些方法呢?细心一点就会发现,我们每次调用属性的时候,get的方法就一定会执行,那么既然他执行了,是不是他就可以知道是谁调用了他呢?
这个时候我们会发现我们即使知道了有方法调用他,也还是一样没有办法具体知道是哪一个方法调用了他,这个时候我们就像,是不是可以设置一个全局的方法,将所有的方法属性都挂载上去
那么调用之前挂载上去,然后他只要被调用,就给一个数组里面塞一条函数进去,这样的话,在 set 的时候将这些方法全部执行一遍不就好了吗?

startObserve() // TODO: 开始收集数据变化会用到的方法
// TODO: 将方法挂到全局的 window 上
window.__ADDFUNC = setName
setName()
window.__ADDFUNC = null

window.__ADDFUNC = setAge
setAge()
window.__ADDFUNC = null

function startObserve(){
	for (const key in data) {
		let __FV = data[key]
		let dependentOns = new Set() 
		Object.defineProperty(data, key, {
			get: function () {
				console.log('🚀 调用了 get()')
				if (window.__ADDFUNC) { dependentOns.add(window.__ADDFUNC) } // TODO: 依赖收集
				return __FV
			},
			set: function (V) {
				console.log(`🚀 数据及时更新,更新的结果是:${V}`)
				__FV = V
				// TODO: 及时更新页面数据
				Array.from(dependentOns).forEach(dependentOn => { dependentOn() }) // TODO: 任务派发执行
			}
		})
	}
}

这样的话 问题基本上就全部解决了,我们只需要将该封装的一部分数据封装起来就可以了,比如所有的方法应该统一进行处理运行,下面是封装之后的代码

// TODO: 实现一个数据响应式  源数据  FILENAME: observer.js
const data = { name: 'tom', age: 16 }
observer(data) // TODO: 观察数据
const methods = {
	setName: () => {
		const name = document.getElementById('name')
		name.innerHTML = data.name
	},
	setAge: () => {
		const age = document.getElementById('age')
		age.innerHTML = data.age
	}
}
// TODO: 将执行的方法使用全局的函数进行包装,依赖收集的时候可以直接使用
function collectionMethodRun(methods) {
	for (const fn in methods) {
		window.OVERALLSITUATION = methods[fn]
		methods[fn]()
		window.OVERALLSITUATION = null
	}
}

collectionMethodRun(methods) //  TODO: 收集方法并执行

// 观察当前的对象信息的变化
function observer(obj) {
	for (const key in obj) {
		let __FV = obj[key]
		let dependentOns = new Set() // TODO: 依赖收集 避免收集到重复的依赖
		Object.defineProperty(obj, key, {
			get: function () {
				if (window.OVERALLSITUATION) { dependentOns.add(window.OVERALLSITUATION) } // TODO: 依赖收集
				return __FV
			},
			set: function (V) {
				__FV = V
				Array.from(dependentOns).forEach(dependentOn => { dependentOn() }) // TODO: 任务派发执行
			}
		})
	}
}

测试一下

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<p id="name"></p>
		<p id="age"></p>
		<script src="./observer.js"></script>
		<span>改变名字:</span>
		<input type="text" oninput="data.name = this.value">
		<span>改变年龄:</span>
		<input type="number" min="0" oninput="data.age = this.value">
	</body>
</html>

在这里插入图片描述

写在后面

通过上面的分析我们可以发现,其实很多看起来很复杂的问题,只要将问题分解成一步一步的,挨个击破即可,不过我还是觉得自己写一遍是比较重要的,不然你看懂了自己写的时候会发现会有很多自己不太理解的地方,今天就这样吧,拜拜

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

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

相关文章

【计算机组成 课程笔记】5.1 处理器的设计步骤

课程链接&#xff1a; 计算机组成_北京大学_中国大学MOOC(慕课) 5 - 1 - 501-处理器的设计步骤&#xff08;14-49--&#xff09;_哔哩哔哩_bilibili 处理器&#xff0c;或者说是CPU&#xff0c;是现代计算机中最为复杂的一个部件。不过先不要劝退&#xff0c;要设计一个简单但是…

1343. 大小为 K 且平均值大于等于阈值的子数组数目

1343. 大小为 K 且平均值大于等于阈值的子数组数目 C代码&#xff1a;滑动窗口 // 窗口长度固定、返回窗口平均值>threshold的窗口个数int numOfSubarrays(int* arr, int arrSize, int k, int threshold){int cnt 0;int l 0;int sum 0;for (int r 0; r < arrSize; r…

轻松制作玩具小程序商城

随着移动互联网的快速发展&#xff0c;小程序成为了各行各业的新宠儿。想要快速搭建一个属于自己的小程序商城吗&#xff1f;乔拓云平台将为你提供最简单的解决方案。下面就跟随我的步骤&#xff0c;一起来学习如何搭建一个玩具小程序商城吧&#xff01; 首先&#xff0c;我们需…

微机原理与技术(2)8086微处理器

8086微处理器 考点介绍功能和介绍后续会继续更新&#xff0c;点赞follow me继续学习 考点介绍 考点一: 掌握8086/8088CPU的功能构成及流水线技术&#xff0c;理解流水线管理规则。2.1 8086/8088 CPU的功能构成2.2 8086/8088CPU的流水线技术考点二:掌握8086/8088CPU寄存器的组成…

论文阅读_大模型_ToolLLM

英文名称: ToolLLM: Facilitating Large Language Models to Master 16000 Real-world APIs 中文名称: TOOLLLM&#xff1a;帮助大语言模型掌握16000多个真实世界的API 文章: http://arxiv.org/abs/2307.16789 代码: https://github.com/OpenBMB/ToolBench 作者: Yujia Qin 日期…

第66步 时间序列建模实战:ARIMA建模(SPSS)

基于WIN10的64位系统演示 一、写在前面 从这一期&#xff0c;我们使用SPSS进行SARIMA模型的构建。 同样&#xff0c;使用某省2005年1月至2016年12月AIDS死亡率的时间序列数据。 二、SPSS建立SARIMA实战 &#xff08;1&#xff09;录入数据和格式调整 双击打开IBM SPSS Sta…

Android 在TextView前面添加多个任意View且不影响换行

实现效果如下&#xff1a; 如上&#xff0c;将头像后面的东西看作一个整体&#xff0c;因为不能影响后面内容的换行&#xff0c;且前面控件的长度是可变的&#xff0c;所以采用自定义View的方法来实现&#xff1a; /*** CSDN深海呐 https://blog.csdn.net/qq_40945489/articl…

通俗易懂讲解大模型:Tokenizer

Tokenizer Tokenizer 是 NLP pipeline 的核心组件之一。Tokenizer 的目标是&#xff1a;将文本转换为模型可以处理的数据。模型只能处理数字&#xff0c;因此 Tokenizer 需要将文本输入转换为数字输入。 通常而言有三种类型的 Tokenizer &#xff1a;Word-based Tokenizer、Cha…

【漏洞复现】广联达办公OAsql+文件上传+弱口令

漏洞描述 广联达办公OA是一款综合办公自动化解决方案,旨在提高组织内部的工作效率和协作能力。它提供了一系列功能和工具,帮助企业管理和处理日常办公任务、流程和文档。默认弱口令admin password,后面就不提了。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当…

Unity 引擎中国版 “团结引擎” 发布

导读Unity 官方宣布&#xff0c;Unity 中国正式推出 Unity 中国版引擎 —— 团结引擎&#xff0c;同时也开启了 Unity 中国本土化进程的全新篇章。作为推动团结引擎落地的核心人物&#xff0c;Unity 中国 CEO 张俊波称致力于将其打造为一款更懂中国开发者的引擎。 团结引擎以 U…

JavaScript基础06——let和var两个关键字有啥不同

哈喽&#xff0c;小伙伴们大家好&#xff0c;我是雷工&#xff01; 每日学习一点点&#xff0c;今天继续学习JavaScript基础知识&#xff0c;下面是学习笔记。 1、变量的本质 内存&#xff1a;计算机中存储数据的地方&#xff0c;相当于一空间。 变量的本质&#xff1a;是程序…

论文阅读:SERE: Exploring Feature Self-relation for Self-supervised Transformer

Related Work Self-supervised 学习目的是在无人工标注的情况下通过自定制的任务&#xff08;hand-crafted pretext tasks&#xff09;学习丰富的表示。 Abstract 使用自监督学习为卷积网络&#xff08;CNN&#xff09;学习表示已经被验证对视觉任务有效。作为CNN的一种替代…

poll epoll初学习

正是select这些缺点&#xff0c;才有了poll 1.I/O多路转接之poll 2.I/O多路转接之epoll 其中的struct epoll_event:

[计算机入门] 项目的压缩与解压缩

3.6 项目的压缩与解压缩 有几个原因使得我们要将文件压缩打包&#xff1a; 节省存储空间&#xff1a;压缩文件能够减小文件的体积&#xff0c;从而节省存储空间。尤其是对于多个大型文件或文件夹进行打包压缩时&#xff0c;可以显著降低所需的存储容量。方便传输&#xff1a;…

gRpc入门

gRpc 一、简介 1、gprc概念 gRpc是有google开源的一个高性能的pc框架&#xff0c;Stubby google内部的rpc,2015年正式开源&#xff0c;云原生时代一个RPC标准。 tips:异构系统&#xff0c;就是不同编程语言的系统。 2、grpc核心设计思路 grpc核心设计思路1. 网络通信 --&g…

C++ string

目录 string类介绍访问&#xff1a;[ ] 遍历迭代器遍历范围for遍历 容量相关&#xff1a;修改相关&#xff1a;编码表的了解写时拷贝的了解string的模拟 STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&a…

【Linux】高级IO --- 多路转接,select,poll,epoll

所有通过捷径所获取的快乐&#xff0c;无论是金钱、性还是名望&#xff0c;最终都会给自己带来痛苦 文章目录 一、五种IO模型1.什么是高效的IO&#xff1f;&#xff08;降低等待的时间比重&#xff09;2.有哪些IO模型&#xff1f;哪些模型是高效的&#xff1f;3.五种IO模型的特…

【韩顺平 零基础30天学会Java】面向对象编程(基础)(5days)

面向对象编程 Java设计者引入类与对象&#xff08;OOP&#xff09;&#xff0c;根本原因就是现有的技术&#xff0c;不能完美的解决新的需求。 对象主要是由属性和行为构成的。 类就是数据类型&#xff08;比如Cat&#xff09;&#xff0c;对象就是具体的实例。 //定义一个猫…

QT转型Visual Studio(qmake项目到cmake项目的移植)

前言 由于工作需要&#xff0c;同时也为了方便以后的集成升级&#xff0c;希望将之前用Qt creator qmake开发的项目移植到cmake项目中&#xff0c;并使用Visual Studio 进行后续开发&#xff0c;本文主要用以记录该过程中的常规步骤和遇到的特殊情况。 qmake项目 在一开始&a…

MATLAB 的 figure 用法总结

文章目录 Syntax&#xff1a;DescriptionExamples1.figure2.figure(Name,Value)Position 属性: 设置 Figure 的位置和大小Name 属性: 设置 Figure 的名称NumberTitle 属性: 取消 Figure 名称里默认的数字units 属性color 属性 3.f figure(___)4.Working with Multiple Figures…