今天学习一下js实现vue.js中的双向绑定

看完后,越来越感觉到JavaScript原生的强大了。

话不多,源码附上

实现效果图:

HTML:

		<div id="app">
			<input type="text" zf-model="msg" />
			<input type="text" zf-model="name"/>
			{{msg}}	{{name}}
			<div>逍遥君</div>
			<div>{{name}}</div>
		</div>
		<div id="app_1">
			
		</div>

js:

		<script>
			//将数据和节点挂载在一起
			let obj = {msg:new ViewModel(''),name:new ViewModel('')};			
			
			function ViewModel(data){
				this.data = data; //this.data代表的是当前的值 (new一个对应一个)
				this.nodes = [];//放节点的盒子
			};
			
			ViewModel.prototype.bindNode = function(node){ //这里要做的事就是把节点和数据绑定在一起(prototype可以让你向对象添加属性和方法)
				this.nodes.push(node);//数组中添加input
//				console.log(this.nodes)
			};
			ViewModel.prototype.setVal = function(newVal){ //这里要做的是将input的值赋给{{}}
				if(newVal !== this.data){
					this.data = newVal;
					this.update()
				};
			};
			ViewModel.prototype.getVal = function(){ //把该方法暴露,取得data的值
				return this.data
			}
			ViewModel.prototype.update = function(){ //更新方法 可以把节点依次渲染成想要的结果
				this.nodes.forEach(node=>{
					if(node.nodeType === 1 ){
						node.value = this.data;
					}else{
						node.textContent = node.my.replace(/\{\{([^}]*)}\}/g,function(){
							return obj[arguments[1]].data;
						})
					}
				})
			}

			function compile(el){
				let ele =document.querySelector(el); //取元素
				//我们不要直接操作节点 可能会导致页面的回流
				let fragment = document.createDocumentFragment() //创建一个文档碎片
				//取ele下的第一个元素	直到取完为止并且将内容放到文档碎片中
				let child;
				while(child = ele.firstChild){
					fragment.appendChild(child);
				};
				function replace(fragment){ //用来递归判断是否有我们想要的标签
				//				NodeList 类数组 Array.prototype.slice.call					
					Array.from(fragment.childNodes).forEach(node=>{	//childNodes取他的儿子 forEach遍历数组
	
	//					console.log(node)
						//判断node 节点是标签 还是文本
						if(node.nodeType === 1){	//元素节点
	//						node.attributes; //取到节点上的所有属性  这还是一个类数组 
							Array.from(node.attributes).forEach(attr=>{
	//							console.dir(attr);  //console.dir 显示一个对象的所有属性和方法
								let {name,value} = attr;  //相当于 let name = attr.name; let value = attr.value
								if(name.includes('zf-')){	//array.includes是判断数组里是否包含某一元素,返回true和false
									obj[value].bindNode(node) //给对应的input添加上bindNode这个方法
									node.addEventListener("input",function(e){
										obj[value].setVal(e.target.value)
									})
								};
							});
							//取得节点上的所有属性
						};
						let reg = /\{\{([^}]*)\}\}/g;
						let text = node.textContent; //取得node文本节点中的数值用来做正则判断
													//test方法用来检索字符串中是否有匹配的文本,有返回true.否则false
						if(node.nodeType === 3 &&reg.test(text)){//文本节点
							text.replace(reg,function(){
//								console.log(arguments)  //获取到函数中的所有参数
								node.my = text ; //自定义属性   保留原有的值
								obj[arguments[1]].bindNode(node);//给对应的text添加上bindNode这个方法
							});
						};
						if(node.childNodes.length){
							replace(node); //如果有嵌套关系 继续查找
						};
					});
				};
				replace(fragment); //编译后要调用update方法
				Object.keys(obj).forEach(key=>{ //Object.keys(),该方法返回一个数组,可以拿到对象的所有属性
					obj[key].update();	
				})
				// 这里操作数据  是不会导致页面回流
				ele.appendChild(fragment);
			};
			compile('#app')
		</script>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值