Vue双向绑定实现

说实话走了一遍其实有的地方还是不太懂有1说1

index.html
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<script src="./Vue.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<div id="app">
			<h2> {{name}}</h2>
			<div>
				<h3>{{age}}</h3>
			</div>
			<input  v-model="name" name="" id="" value="" />
		</div>
	</body>
	<script type="text/javascript">
	 const app = 	new Vue({
			el:"#app",
			data:{
				name:"11",
				age:"22",
				info:{
					a:"as",
					b:"ad2w"
				}
			},
			methods:function(){
				
			}
		})
	
	console.log(app)	
	</script>
</html>

vue.js
class Vue {
	constructor(options) {
		 this.$data = options.data;
		 //调用数据劫持
		 Observe(this.$data);
		 
		 //属性代理 直接在 Vue实例上获取属性 少去.$data在点属性的取值
		 Object.keys(this.$data).forEach((key)=>{
			 	 Object.defineProperty(this, key, {
					 enumerable: true,
					 configurable: true,
					 get: function() {
					 	return this.$data[key]
					 },
					 set: function(newVal) {
					 	 this.$data[key] =newVal;
					 }
				 });
		 })
		 
		 //调用模板编辑
		 compile(options.el,this);
	}
}

// 定义一个数据劫持方法
function Observe(obj){
	//递归的终止条件
	if(!obj || typeof  obj !="object") return;
	
	 const dep = new Dep();
	//获取到对象上的每个属性
	let objkey = Object.keys(obj);
	objkey.forEach((key)=>{
		   //当前循环属性对应的值
		   let value = obj[key];
		     
		//再次递归循环对象里面的属性
			Observe(value);
		  //为每一个属性添加上getter 和setter属性
		 Object.defineProperty(obj, key, {
		     enumerable: true,
		     configurable: true,
		     get: function() {
				 //返回对应的值
				 console.log(`有人获取了$(key)的值`)
				 //刚才new 的Watcher实例就被存放到了 dep.subs这个数组中
				 Dep.target && dep.addSub(Dep.target);
		         return  value;
		     },
		     set: function(newVal) {
				 console.log(`有人设置了值$(newVal)`) 
		         value= newVal;
				 Observe(value);
				 //通知每个订阅者更新自己
				 dep.notify()
		     }
		 });
	})
}



//编译模板
function compile(el,vm){
	  //获取dom元素 挂载到$el身上
      vm.$el = 	document.querySelector(el);
	  
	  //创建一个文档碎片,提高dom 效率
	  const  xunidom = document.createDocumentFragment();
	  
	  while((childNode = vm.$el.firstChild)){
		   xunidom.appendChild(childNode)
	  }
	  
	  //进行模板编译
	  repalcetemp(xunidom)
	  //在渲染到页面上
	   vm.$el.appendChild(xunidom);
	   
	   //负责对dom模板进行编辑
	   function repalcetemp(node){
		   //定义插值正则表达式
		   const regMustache = /\{\{\s*(\S+)\s*\}\}/;
		   
		   //证明当前文本节点node 节点是一个文本节点 需要正则替换
		   if(node.nodeType ===3){
			   const text = node.textContent;
			   //进行正则匹配提取
			   const execResult  = regMustache.exec(text);
			   if(execResult){
				 const value  =  execResult[1].split(".").reduce((nreobj,key)=>nreobj[key],vm)
			     node.textContent = text.replace(regMustache,value);
				 
				 //在这个时候 创建Watcher 类的实例
				 	new Watcher(vm, execResult[1],(newValue)=>{
						 node.textContent = text.replace(regMustache,newValue)
					});
					
			   }
			   return
		   }
		   
		   //判断当前Node节点是否是input输入框
		   if(node.nodeType ===1 && node.tagName.toUpperCase()=="INPUT"){
			   //得到当前元素的所有属性节点
			   const attrs = Array.from(node.attributes);
			   const findResult =attrs.find((x)=>x.name ==='v-model');
			   
			   if(findResult){
				  const expstr = findResult.value;
				   const value = expstr.split(".").reduce((newobj,k)=>newobj[k],vm)
				   node.value =value;
				   new Watcher(vm, expstr,(newValue)=>{
				     node.value =newValue;
				   });
				   
				   
				   //监听输入事件 更新值 同步dao vm上
				   node.addEventListener("input",(e)=>{
					   const val = e.target.value;
					   const keyArr= expstr.split(".");
					   const obj =    keyArr.slice(0,keyArr.length-1).reduce((newobj,k)=>newobj[k],vm)
				        obj[keyArr[keyArr.length-1]] = val;
				   })
			   }
		   }
		
		 //递归调用
		  node.childNodes.forEach((child)=>repalcetemp(child))
	   }
}

		
	//收集依赖 收集订阅
	class Dep{
		constructor() {
			  this.subs=[];
		}
		
		//像subs 中添加订阅信息
		addSub(watcher){
			this.subs.push(watcher)
		}
		//发布订阅方法 负责通知每个watcher 的方法
		notify(){
			 this.subs.forEach((watcher)=>watcher.update())
		}
	}
	

	//订阅的类
	class Watcher{
		constructor(vm,key,cb) {
			//cb 回调函数中,记录 Watcher如何更新自己的文本内容
			this.cb=cb;
			this.key=key;
			this.vm=vm;
			
			// 负责把创建的Watcher 实例存储到Dep实例的subs数组中
			Dep.target =this;
			key.split(".").reduce((newobj,k)=> newobj[k],vm)
			Dep.target = null;
		}
		//触发回调 
		update(){
		    const val = this.key.split(".").reduce((newobj,k)=> newobj[k],this.vm);
			this.cb(val);
		}
	}
	














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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1登峰造极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值