一、概念
①常见的架构方式或者开发思想还有MVC、MVP和MVVM,参考文章:MVC,MVP 和 MVVM 的图示
②MVC:视图(View),指用户界面;控制器(Controller),指业务逻辑;模型(Model),指数据保存
③MVP:将 Controller 改名为 Presenter,同时改变了通信方向
④MVVM:M,即model 业务数据模型,指操作数据的类;V,即view 视图界面,HTML用户界面;VM,viewModel 视图数据模型,指驱动视图改变的data数据。核心思想就是数据驱动视图。
二、MVVM的优点
①低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
②可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
③独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码。
④可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
三、vue原理简单剖析
<div id="app"> {{message}} <h1>{{message}}</h1> </div> <script> function Vue(options){ var {el,data}=options; // 根节点 var rootEl=document.querySelector(el); var childNodes=rootEl.childNodes; var domAndKey={ }; // 编译模板 for(var i=0;i<childNodes.length;i++){ let node=childNodes[i]; switch(node.nodeType){ case 1: //console.log(node,'标签节点') break case 3: //console.log(node,'文本节点') var matches=/{{(.+)}}/.exec(node.textContent); if(matches){ var dataKey=matches[1].trim(); //console.log(dataKey); // 记住这个节点以及这个节点所绑定的数据成员 domAndKey[dataKey] = node; } break default: break } } // 数据观测 for(let key in data){ Object.defineProperty(this,key,{ get(){ }, set(val){ domAndKey['message'].textContent = val } }) } } var app =new Vue({ el:'#app', data:{ message:'hello vue' }, }); </script>
四、双向绑定原理
①事件订阅发布模型
// 设计模式:事件发布/订阅模型 /* 构造函数 */ function EventEmit(){ this.callbacks={} }; /* 添加原型方法 */ EventEmit.prototype.on=function(eventName,fn){ if(!this.callbacks[eventName]){ this.callbacks[eventName]=[] } this.callbacks[eventName].push(fn) }; /* 添加原型方法 */ EventEmit.prototype.emit=function(eventName){ if(!this.callbacks[eventName]){ return } this.callbacks[eventName].forEach(fn => { fn() }); } //创建实例对象 var el=new EventEmit(); // 订阅事件 el.on('message',function(){ console.log('aaa') }) el.on('message',function(){ console.log('bbb') }) el.on('message',function(){ console.log('ccc') }) // 发布事件 el.emit('a')
②双向绑定实现
<div id="app"> {{message}} <input type="text" v-model='message'> <h1>{{message}}</h1> <div> <div>{{message}}</div> <div> <h1>{{message}}</h1> </div> </div> </div> <!-- 引入事件订阅发布模型文件 --> <script src="eventemit.js"></script><!-- --> <script> //双向绑定实现原理的核心就是Object.defineProperty和事件订阅发布模型 (function(){ function Vue(options){ var {el,data}=options; // 根节点 var rootEl=document.querySelector(el); var _date={}; var _event=new EventEmit(); // 数据观测:当date中的数据发生变化时,发出事件通知,所有订阅了该事件的DOM都会得到更新 for(let key in data){ _date[key]=data[key];//把data中的数据往_data里复制一份,当通过 实例访问data中的数据时,实际上访问的是_data中的数据 Object.defineProperty(this,key,{ get(){ return _data[key] }, set(val){ _date[key]=val; _event.emit(key); }, }); } // 递归解析模板,注册数据绑定事件 function compile(childNodes){ childNodes.forEach((node,index) => { switch(node.nodeType){ case 1: //处理input类型的标签 if(node.nodeName==='INPUT'){ const vModel=node.attributes['v-model']; if(!vModel){ return } var dataKey=vModel.value.trim(); node.oninput=()=>{ this[dataKey]=node.value; } } compile.call(this,node.childNodes);//标签节点继续递归调用 break case 3: var matches=/{{(.+)}}/.exec(node.textContent); if(matches){ var dataKey=matches[1].trim(); _event.on(dataKey,()=>{ node.textContent=_date[dataKey]; }) } break } }); } compile.call(this,rootEl.childNodes) } window.Vue=Vue })(); var app =new Vue({ el:'#app', data:{ message:'hello vue', }, }); </script>