js实现双向数据绑定

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>vue双向数据绑定</title>
		<script type="text/javascript">
			class Vue{
				constructor(options){
					this.options = options;
					// 获取data对象
					this.$data = options.data;
					// 获取定义的el元素(#app)
					this.$el = document.querySelector(options.el);
					// 定义容器收集订阅者
					this._directives = {};
					// 数据劫持
					this.Observer(this.$data);
					// 解析指令
					this.Compile(this.$el);
				}
				
				Observer(data){
					//初始化订阅者容器数组,有多少属性就准备多少数组
					for (let key in data) {
						//遍历对象并存入 _directives对象中,例如: { myText:[], myGod:[] }
						this._directives[key] = [];
						// 当前值(旧的值)
						let Val = data[key];
						let _obj = this._directives[key];
						//第一个参数:当前对象,第二个参数:当前对象的属性,第三个参数:
						Object.defineProperty(this.$data,key,{
							get: function(){
								return Val;
							},
							// set方法里的参数是默认自带的(名字自定义)表示新的值
							set: function(newVal){
								// 如果旧的值与新的值不相等
								if( Val !== newVal ){
									// 旧的值从新赋值(新的值给旧的值)
									Val = newVal;
									// 数组遍历
									_obj.forEach( element =>{
										element.update();
									} );
								}
							}
						})
					}
					console.log(this._directives)
				}
				
				Compile(el){
					// 获取app下面的所有子元素
					let nodes = el.children;
					//console.log(nodes)
					
					//遍历元素集合nodes
					for(let i = 0; i < nodes.length; i++){
						// node[i]是元素对象
						let node = nodes[i];
						// 如果当前元素的子元素长度大于零
					    if(node.children.length){
							// 递归(自己调用自己)
							this.Compile(node);
						}
						// 如果node元素上有v-text属性
						if(node.hasAttribute("v-text")){
							// 获取v-text属性值
							let attrValue = node.getAttribute("v-text");
							// 往订阅者_directives里的对应数组添加Watcher实例对象(就是{ myText:[], myGod:[] }里的myText: [] )
							this._directives[attrValue].push(new Watcher(node,this,attrValue,"innerHTML"))
							
						}
						// 文本框元素才可以绑定v-model
						if(node.hasAttribute("v-model")){
							let _this = this;
							// 获取v-text属性值
							let attrValue = node.getAttribute("v-model");
							// 往订阅者_directives里的对应数组添加Watcher实例对象(就是{ myText:[], myGod:[] }里的myText: [] )
							this._directives[attrValue].push(new Watcher(node,this,attrValue,"value"))
							
							/**
							 * EventTarget.addEventListener() 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,
							 * 指定的回调函数就会被执行。 事件目标可以是一个文档上的元素 Element,Document和Window或者任何其他支持
							 * 事件的对象 (比如 XMLHttpRequest),参数有多个,这里就讲常用的两个。
							 * 		第一个参数:监听的事件类型(很多),查看 https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
							 * 		第二个参数:事件处理函数
							*/ 
						   // 监听input值发生变化
							node.addEventListener("input",(function(){
								// input值发生变化就会执行下面代码
								return function(){
									// console.log(node.value)
									
									//把node.value(输入框里的文本值)赋值给data里的属性如: myText,myGod
									_this.$data[attrValue] = node.value;
									
								}
							})())
						}
					}
				}
				
			}
			
			// 订阅者对象,负责更新
			class Watcher{
				constructor(el,vm,exp,attr){
					this.el = el;
					this.vm = vm;
					this.exp = exp;
					this.attr = attr;
					// 调用更新方法
					this.update();
				}
				//更新
				update(){
					this.el[this.attr] = this.vm.$data[this.exp];
				}
			}
		</script>
	</head>
	<body>
		<div id="app">
			<h1>双向数据绑定</h1>
			<div class="">
				<div v-text="myText"></div>
				<input type="text" v-model="myText" />
			</div>
		</div>
		<script type="text/javascript">
			const app = new Vue({
				el: "#app",
				data: {
					myText: 'vue双向数据绑定',
					myGod: '大吉大利'
				}
			})
		</script>
	</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值