美文网首页
深入理解vue的双向数据绑定

深入理解vue的双向数据绑定

作者: meteornnnight | 来源:发表于2019-08-17 15:15 被阅读0次

    reference:

    <div id="app>
      <input type="text" v-model="text">
      {{text}}
    </div>
    <script>
      const vm=new Vue({
        el: 'app',
        data: {
        text: 'hello'
        }
      });
    </script>
    

    1. 我们先来完成第一步,也就是model=>view这一层的渲染。

    function updateNode(node,vm){
      var frag=document.createDocumentFrag();
      var child=null;
      while(child=node.firstChild){
        compile(child,vm);
        frag.appendChild(child);
      }
    //这里我们可以观察到一个细节:在所有的子节点都更新完成之后,再去更新dom
      node.append(frag);
    }
    function compile(node,vm){
      var reg=/\{\{(.*)\}\}/
      if(node.nodeType==1){
        var attrs=node.attrbutes; // a nodeList
        for(i=0;i<attrs.length;i++){
          if(attrs[i].nodeName=='v-model'){
            node.value=vm.data[attrs[i].nodeValue];
            node.removeAttribute('v-model');
          }
        }
      }
      if(node.nodeType==3){
        if(reg.test(node.nodeValue){
          var name=RegExp.$1;
          name=name.trim();
          node.nodeValue=vm.data[name];
        }
      }
    }
    //Vue constructor
    function Vue(options){
      var id=options.el;
      this.data=options.data;
      updateNode(document.getElementById(id),this);
    }
    

    搞定!是不是很简单!

    2. 下面我们来看第二步:view=>model

    几个需要预先领会的point:

    • 这里我们把vm对象中的属性用Object.defineProperty()来定义。vm.data[property1]=newVal这个赋值语句执行的时候,会调用set()函数,我们就可以在set函数里进行操作。
    • 发布订阅模式
    var publisher={
      publish: function(){
        dep.notify();
      }
    }
    sub1={update:function(){...}};
    sub1={update:function(){...}};
    sub1={update:function(){...}};
    function Dep{
      this.subs=[sub1,sub2,sub3];
    }
    Dep.prototype.notify=function(){
      this.subs.forEach(sub=>{
        sub.update();
      )
    }
    

    发布订阅模式与双向绑定的关系:我们为vue实例data中的每个属性都分配一个dep对象;同时在编译html的时候,把与data相关的节点分配一个watcher对象。这里的关键在于,如果把节点对应的watcher对象添加到dep中。
    我们需要在watcher的构造函数里做这些事:

    function Watcher(vm,node,name){
      Dep.target=this;
      this.name=name;
      this.vm=vm;
      this.update();
      Dep.target=null;
    }
    Watcher.prototype={
      update: function(){
        this.get();
        this.node.nodeValue=this.value;
      }
      get: function(){
        this.value=this.vm[this.name];
      }
    }
    

    compile()函数有关的部分:我们为v-model元素添加事件,并且为data属性对应的节点生成对应的watcher对象。

    function(node,vm){
      if(node.nodeType==1){
        var attrs=node.attributes;
        for(var i=0;i<attrs.length;i++){
        if(attrs[i].nodeName='v-model'){
          node.addEventListener('change',e=>vm[attrs[i].nodeValue]=e.target.value)
          }
        }
      }
      if(node.nodeType==3){
        if(reg.test(node.nodeValue)){
          var name=RegExp.$1;
          name=name.trim();
          new Watcher(node,vm,name);
          }
        }
    }
    

    vm定义data中某个特性:

    function defineReactive(obj,key,val){
      var dep=new Dep();
      Object.defineProperty(obj,keyName,{
        get: function(){
           if(Dep.target) dep.addSub(Dep.target);
        }
        set: function(newVal){
          if(newVal==val) return;
          val=newVal;
          dep.notify();
        }
      }
    }
    

    双向绑定大致的process整理

    1. 创建某一个vue实例,vue实例中data中的每个属性都会创建一个dep对象与之对应;
    2. vue实例编译对应的html模板,某一个节点如果用到data的某一个属性,就会创建一个它自己的watcher对象,在这个构造函数里,读取对应的data值,更新视图,同时这个过程调用了get函数,所以会将watch对象添加到dep对象中。
    3. 当每一次data属性更新的时候,set函数会调用notify函数,通知dep中的每个成员对象更新。

    相关文章

      网友评论

          本文标题:深入理解vue的双向数据绑定

          本文链接:https://www.haomeiwen.com/subject/dghzjctx.html