Vue - 响应式模拟 - Vue2.x

本文通过JavaScript模拟Vue2.x的响应式系统,使用观察者模式和数据劫持技术,通过Dep类管理观察者,Watcher类监听数据变化并更新视图。当数据发生变化时,会触发setter并通知所有订阅者进行更新。
摘要由CSDN通过智能技术生成

模拟Vue2.x的响应式系统

Vue 2.x主要采用的是观察者模式。在Vue 2.x中,当数据发生变化时,会触发视图的更新,这主要是通过观察者模式实现的。具体来说,Vue 2.x通过数据劫持的方式,使用Object.defineProperty方法重写对象属性的gettersetter,从而实现对数据变化的监听。当数据变化时,会触发setter方法,进而通知观察者(Watcher实例)进行更新操作。

下面通过原生HTML+JavaScript模仿Vue 2.x的响应式

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Simple Reactive System</title>
</head>
<body>
<div id="app">
	<label for="inputField">例:</label><input type="text" id="inputField" />
	<p id="message"></p>
</div>
</body>
</html>
// 定义一个Dep类,用于管理订阅者
class Dep {
	// 初始化存储订阅者(即Watcher实例)
	constructor () {
		// 使用Set来存储观察者,确保唯一性
		this.subscribers = new Set();
	}
	
	// 添加订阅者
	addSub (sub) {
		if (!this.subscribers.has(sub)) { // 检查观察者是否已经订阅
			this.subscribers.add(sub);
		}
	}
	
	// 取消订阅
	// unSub(sub) {
	// 	this.subscribers.delete(sub);
	// }
	
	// 通知所有订阅者更新
	notify () {
		this.subscribers.forEach(sub => {
			sub.update();
		});
	}
}

// 定义一个Watcher类,用于观察数据变化并更新视图
class Watcher {
	/**
	 * 构造函数
	 * @param vm 视图模型,通常是数据对象
	 * @param exp 表达式,用于从视图模型中获取数据
	 * @param cb 回调函数,当数据变化时调用
	 */
	constructor (vm, exp, cb) {
		this.cb = cb;
		this.vm = vm;
		this.exp = exp;
		this.value = this.get(); // 初始化当前值,并触发getter,收集订阅者
	}
	
	/**
	 * 获取数据,并触发getter以收集订阅者
	 * @returns {*}
	 */
	get () {
		console.log(this.vm)
		Dep.target = this; // 将当前Watcher实例设置为Dep.target,用于收集订阅者
		const value = this.vm[this.exp]; // 通过表达式从视图模型中获取数据
		Dep.target = null; // 重置Dep.target为null
		return value; // 返回获取到的数据
	}
	
	/**
	 * 更新方法,调用run方法更新视图
	 */
	update () {
		this.run();
	}
	
	/**
	 * 运行方法,用于比较新旧值并更新视图
	 */
	run () {
		// 重新获取值来与旧值比较
		const value = this.get();
		if (value !== this.value) {
			this.value = value;
			// 调用回调函数并传入新值和表达式
			this.cb.call(this.vm, value, this.exp);
		}
	}
}

// data对象,包含message属性
const data = {
	message: ''
};

// 创建一个Dep实例来管理message属性的订阅者
const dep = new Dep();
// 定义value变量,用于存储message属性的实际值
let value;

// 使用Object.defineProperty来劫持data对象的message属性的getter和setter
Object.defineProperty(data, 'message', {
	enumerable: true,   // 属性可枚举
	configurable: true, // 属性可配置
	get () {
		if (Dep.target) {
			// 将当前Watcher添加到dep的订阅者数组中
			dep.addSub(Dep.target);
		}
		return value; // 返回value变量的值
	},
	
	set (newValue) {
		if (newValue === value) return;
		value = newValue;   // 更新message属性的值为新值
		dep.notify();       // 通知所有订阅者(Watcher)更新
	}
});

// 创建一个Watcher实例来观察message属性的变化
new Watcher(data, 'message', function (newVal) {
	const messageElement = document.getElementById('message');
	if (messageElement) {
		messageElement.textContent = newVal;
	}
});

// 监听输入事件
const inputField = document.getElementById('inputField');
inputField.addEventListener('input', (event) => {
	data.message = event.target.value; // 更新data.message的值
});

观察者模式是一种软件设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,它的所有依赖者(观察者)都会自动收到通知并更新。

在观察者模式中:

  • 主题(Subject) 或称为 发布者(Publisher):维护一个观察者列表,当状态发生变化时,会通知所有注册的观察者。
  • 观察者(Observer) 或称为 订阅者(Subscriber):当主题状态改变时,会被通知并执行相应的操作。

在模拟的代码中:

  • Dep 类扮演了主题(Subject)的角色,维护了一个订阅者列表(subscribers),并提供了添加订阅者(addSub)和通知订阅者更新(notify)的方法。
  • Watcher 类扮演了观察者(Observer)的角色,它有一个更新方法(update),当主题(Dep 实例)状态改变时,该方法会被调用。
  • Object.defineProperty 被用来劫持 data 对象中 message 属性的gettersetter。当 message 属性被访问时(即getter被触发),如果当前有 Watcher 正在收集依赖(Dep.target 不为 null),则将该 Watcher 添加到 Dep 实例的订阅者列表中。当 message 属性的值被改变时(即setter被触发),Dep 实例会通知所有订阅者(Watcher)更新。

在这个简单的响应式系统中,观察者(Watcher)订阅了主题(Dep),当主题状态改变时也就是( data.message 的值发生变化时),所有订阅的观察者都会收到通知并更新自身状态。通过 Object.defineProperty 劫持的setter会被触发,进而调用 dep.notify() 方法通知所有订阅的 Watcher 实例。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值