前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一步一步分析将数据响应式实现出来

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

作者头像
何处锦绣不灰堆
发布2023-10-18 17:44:45
1210
发布2023-10-18 17:44:45
举报
文章被收录于专栏:农历七月廿一
写在前面

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

数据响应式

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

实现一个数据响应式需要具备的条件
  • 需要一个方法设置数据变化的时候映射到页面
  • 需要一个方法数据变化要可以及时调对应的方法
难点是什么?
  • 怎么知道数据变化了?
  • 数据变化之后怎么知道是哪个方法该更新?
实现第一件事
代码语言:javascript
复制
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
}
实现第二件事
代码语言:javascript
复制
setName()
setAge()

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

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

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

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

代码语言:javascript
复制
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 的时候将这些方法全部执行一遍不就好了吗?

代码语言:javascript
复制
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: 任务派发执行
			}
		})
	}
}

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

代码语言:javascript
复制
// 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: 任务派发执行
			}
		})
	}
}
测试一下
代码语言:javascript
复制
<!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>
写在后面

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
    • 数据响应式
    • 实现一个数据响应式需要具备的条件
    • 难点是什么?
    • 实现第一件事
    • 实现第二件事
    • 解决怎么知道数据变化了的问题
    • 怎么知道是哪些方法需要调用呢?
    • 测试一下
    • 写在后面
    相关产品与服务
    腾讯云服务器利旧
    云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档