重温Vue数据绑定原理(幼儿园版)

16 篇文章 0 订阅

在这里插入图片描述

<head></head>

<body>
	<div id="app"> 
		<input id="editName" type="text">
		<br />
		name:<span id="name"></span>

		<div>------------------------------</div>

		<input id="editAge" type="text">
		<br />
		age:<span id="age"></span>
	</div>
</body>

<script type="text/javascript">
	function Dep() {
		this.subs = [];
	}
	Dep.prototype = {
		addSub: function (sub) {
			this.subs.push(sub);
		},
		notify: function () {
			// 依次通知
			this.subs.forEach(sub => {
				sub.update();
			})
		}
	}

	// vm是vue实例
	// exp指令的属性值 比如v-model='name'的name
	// cb是更新函数
	function Watcher(vm, exp, cb) {
		this.vm = vm;
		this.exp = exp;
		this.cb = cb;
		this.value = this.get();  // 将自己添加到订阅器的操作
	}

	Watcher.prototype = {
		update: function () {
			var value = this.vm.data[this.exp];  //data.name 可能已经更改的数据
			var oldVal = this.value; // 添加订阅者时初始化的数据
			// 判断是否需要更新
			if (value !== oldVal) {
				this.value = value;
				this.cb.call(this.vm, value, oldVal);
			}
		},
		get: function () {
			Dep.target = this;  // 缓存自己 (一个Watcher实例)
			var value = this.vm.data[this.exp]  // 如selfVue.data['name'] 触发Observe(defineProxy)中的get,收集依赖
			Dep.target = null;  // 释放自己 由于target是Dep的原型变量,以免影响其他观察者
			return value;
		}
	};

	function defineProxy(obj, key, value) {
		// 递归所有子属性
		Observe(value);

		// 对于每一个实例,都有一个订阅者数组
		let dep = new Dep();

		// 每调用一次defineProxy,就产生一个该函数上下文,保存这个val
		let val = value;
		Object.defineProperty(obj, key, {
			enumerable: true,
			configurable: true,
			set(newVal) {
				if (val === newVal) {
					return;
				}
				console.log(`${val}=>${newVal}`)
				val = newVal;
				dep.notify(); //通知订阅者
			},
			get() {
				// 在Watcher.get()中触发到这里
				if (Dep.target) {
					// 把该watcher加到订阅者中
					dep.addSub(Dep.target)
					console.log(dep.subs)
				}
				return val;
			}
		})
	}
	function Observe(data) {
		if (!data || typeof data !== 'object') {
			return;
		}
		Object.keys(data).forEach(function (key) {
			defineProxy(data, key, data[key]);
		});
	}

	function SelfVue(options) {

		let data = options.data
		this.data = data
		Observe(this.data);

		Object.keys(data).forEach(key => {
			let el = document.getElementById(key) || null
			// 还要判断是什么类型的dom , 根据不同的dom去设置不同的回调函数
			if (el) {
				el.innerHTML = data[key] // 初始化

				// 为该数据创建一个Watcher
				// 一个数据/dom节点对应的应该就是一个watch对象
				new Watcher(this, key, function (value,oldValue) {
					el.innerHTML = value;
				});
			}
		})
		return this;
	}

	var selfVue = new SelfVue({
		el: '#app',
		data: {
			name: 'jenson',
			age: 20
		}
	});

	let inputNameDom = document.getElementById('editName')
	inputNameDom.oninput = (e) => {
		selfVue.data.name = inputNameDom.value; //触发data的set拦截器
	}
	let inputAgeDom = document.getElementById('editAge')
	inputAgeDom.oninput = (e) => {
		selfVue.data.age = inputAgeDom.value; //触发data的set拦截器
	}

</script>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值