vue源码之数据双向绑定

15 篇文章 0 订阅
14 篇文章 0 订阅

实现思路

  1. 数据 -> 响应式的数据 Object.defineProperty Proxy
  2. input -> input/keyup -> 事件处理函数的绑定 -> 改变数据
  3. 相关的DOM -> 数据 => 绑定在一起
    操作数据的某个属性 -> 对应DOM就改变

首先建一个html页面

<!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>Document</title>
	</head>
	<body>
		<div id="app">
			<!-- 逻辑改变了数据、数据和视图是相互改变,相互影响的过程   v-model -->
			<div>
				<input type="text" v-model="name" placeholder="姓名" />
				<input type="text" v-model="age" placeholder="年龄" />
			</div>
			<div>
				<p>姓名: <span>{{ name }}</span></p>
				<p>年龄: <span>{{ age }}</span></p>
			</div>
			<button id="btn">改变姓名</button>
		</div>

		<script src="mvvm.js"></script>
		<script>
			const app = new MVVM('#app', {
				name: '',
				age: '',
			});

			document
				.querySelector('#btn')
				.addEventListener('click', function () {
					app.setData('name', '久奈西野');
				});
		</script>
	</body>
</html>

MVVM.js

class MVVM {
	constructor(el, data) {
		this.el = document.querySelector(el);
		this._data = data;
		this.domPool = {}; //作用于dom和数据的绑定
		this.init();
	}
	init() {
		this.initData();
		this.initDom();
	}
	initDom() {
		this.bindDom(this.el);
		this.bindInput(this.el);
	}
	initData() {
		const that = this;
		this.directive = {}; //在实例上新增一个data空对象

		// 方法1
		// for (const key in this._data) {
		// 	Object.defineProperty(this.directive, key, {
		// 		get() {
		// 			// console.log('获取数据:', key, that._data[key]);
		// 			return that._data[key];
		// 		},
		// 		set(newValue) {
		// 			// console.log('设置数据:', key, newValue);
		// 			that.domPool[key].innerHTML = newValue;
		// 			that._data[key] = newValue;
		// 		},
		// 	});
		// }

		//Proxy方法 方法2
		this.directive = new Proxy(this._data, {
			get(target, key) {
				return Reflect.get(target, key);
			},
			set(target, key, value) {
				that.domPool[key].innerHTML = value;
				return Reflect.set(target, key, value);
			},
		});
	}
	bindDom(el) {
		const childNodes = el.childNodes;
		childNodes.forEach((item) => {
			// 判断是否为文本节点
			if (item.nodeType === 3) {
				const _value = item.nodeValue;
				if (_value.trim().length) {
					let _isValid = /\{\{(.+?)\}\}/.test(_value);

					if (_isValid) {
						const _key = _value.match(/\{\{(.+?)\}\}/)[1].trim();
						this.domPool[_key] = item.parentNode;
						item.parentNode.innerText = this.directive[_key] || '';
					}
				}
			}

			// 递归
			item.childNodes && this.bindDom(item);
		});
	}

	// 为DOM节点添加指令事件
	bindInput(el) {
		const _allInputs = el.querySelectorAll('input');

		_allInputs.forEach((input) => {
			const _vModel = input.getAttribute('v-model');

			if (_vModel) {
				input.addEventListener(
					'keyup',
					this.handleInput.bind(this, _vModel, input),
					false
				);
			}
		});
	}

	// 更新数据
	handleInput(key, input) {
		const _value = input.value;
		this.directive[key] = _value;
		// console.log(this.directive);
	}

	setData(key, value) {
		this.directive[key] = value;
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值