当前位置: 首页 > Web前端 > vue.js

vue双向数据绑定示意图(简单)

时间:2023-03-31 23:35:37 vue.js

双向数据绑定的概念,相信大家都不陌生,简单来说就是数据变化更新视图,视图变化更新数据。为了达到这个效果,在Vue中,采用了数据劫持结合发布订阅模式。通过Object.defineProperty()实现数据劫持,监听数据变化。通过发布者Dep()和订阅者Watcher实现发布订阅者模式,从而实现视图与数据相互更新的解耦。关于如何实现简单的双向数据绑定的例子网上有很多,我就不一一列举了。这里我画一下我理解的双向绑定原理:然后,我把代码分模块贴出来(代码不是我写的,我也找了别人的研究)observer()functionobserver(data){if(!data||数据类型!=='object'){return;}Object.keys(data).forEach(key=>{vardep=newDep();varvalue=data[key];observer(value);Object.defineProperty(data,key,{configurable:true,enumerable:true,get(){if(Dep.target){dep.addSub(Dep.target)}returnvalue;},set(newValue){if(value===newValue){return;}value=newValue;dep.notify();},})})}Dep()functionDep(){this.subs=[];}Dep.prototype={addSub:function(sub){this.subs.push(sub);},通知:function(){this.subs.forEach(sub=>{sub.update();});}}Dep.target=null;WatcherfunctionWatcher(vm,exp,cb){this.vm=vm;这个.exp=exp;这个.cb=cb;this.value=this.get();}Watcher.prototype={get:function(){Dep.目标=这个;varvalue=this.vm._data;this.exp.split('.').forEach(key=>{value=value[key]})Dep.target=null;返回值;},更新:function(){this.run();},运行:function(){varvalue=this.vm._data;this.exp.split('.').forEach(key=>{value=value[key]})if(value!==this.value){this.value=value;this.cb.call(this.vm,value);}}}CompilefunctionCompile(el,vm){this.el=document.querySelector(el);这个.vm=虚拟机;this.init();}Compile.prototype={init:function(){this.fragment=this.node2Fragment(this.el);this.compile(this.fragment);this.el.appendChild(this.fragment);},node2Fragment:function(el){varfragment=document.createDocumentFragment();varchild=el.firstChild;while(child){fragment.appendChild(child);孩子=el.firstChild;}返回片段;},编译:function(el){varchildNodes=el.childNodes;那个=这个;Array.prototype.slice.call(childNodes).forEach(node=>{//if(that.isElementNode(node)){//that.compileElement(node)//}if(that.isTextNode(node)){that.compileText(node)}if(node.childNodes&&node.childNodes.length){that.compile(node)}})},compileElement:function(node){varattributes=node.attributes;那个=这个;Array.prototype.forEach.call(attributes,function(attr){if(that.isDirective(attr)){if(that.isModelDirective){that.compileModel()}if(that.isHtmlDirective){that.compileHtml()}if(that.isEventDirective){that.compileEvnet()}}//if(that.isShortEventDirective(attr)){//that.compileEvnet()//}});},compileText:function(node){varthat=this;varreg=/\{\{(.*)\}\}/;如果(reg.test(node.textContent)){varexp=reg.exec(node.textContent)[1].trim()varval=that.vm._data;exp.split('.').forEach(key=>{val=val[key]})that.updateText(node,val);newWatcher(that.vm,exp,function(value){that.updateText(node,value)})}},compileModel:function(){},compileHtml:function(){},compileEvnet:function(){},updateText:function(node,value){node.textContent=value},isDirective:function(attr){returnattr.indexof('v-')===0;},isEventDirective:function(attr){returnattr.indexof('on:')===0;},isShortEventDirective:function(attr){returnattr.indexof('@')===0;},isHtmlDirective:function(dir){returndir.indexof('html')===0;},isModelDirective:function(dir){returndir.indexof('model')===0;},isElementNode:function(node){返回node.nodeType===1;},isTextNode:function(node){返回节点。节点类型===3;}}VuefunctionVue(options={}){this.$options=options;this.$el=document.getElementById(options.el);this._data=options.data;让数据=这个._数据;this._proxyData(options.data);观察者(数据);this.methods=options.methods;newCompile(options.el,this)}Vue.prototype={_proxyData:function(data){varthat=this;Object.keys(data).forEach(key=>{Object.defineProperty(this,key,{configurable:true,enumerable:false,get(){returnthat._data[key];},set(newValue){that._data[key]=newValue;},})})}}测试

{{msg}}
{{f.name}}
varvm=newVue({el:'#app',data:{msg:'11',f:{name:1},dom:''},方法:{clickMe(){console.log(123);}}})在浏览器控制台输入vm.msg=333;vm.f.name='xxxxx'可以看到数据已经改变