真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Vue2.0如何實(shí)現(xiàn)雙向綁定

今天小編給大家分享一下Vue2.0如何實(shí)現(xiàn)雙向綁定的相關(guān)知識點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

公司主營業(yè)務(wù):網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出甘南免費(fèi)做網(wǎng)站回饋大家。

一、實(shí)現(xiàn)雙向綁定的做法

前端MVVM最令人激動(dòng)的就是雙向綁定機(jī)制了,實(shí)現(xiàn)雙向數(shù)據(jù)綁定的做法大致有如下三種:

1.發(fā)布者-訂閱者模式(backbone.js)

思路:使用自定義的data屬性在HTML代碼中指明綁定。所有綁定起來的JavaScript對象以及DOM元素都將“訂閱”一個(gè)發(fā)布者對象。任何時(shí)候如果JavaScript對象或者一個(gè)HTML輸入字段被偵測到發(fā)生了變化,我們將代理事件到發(fā)布者-訂閱者模式,這會(huì)反過來將變化廣播并傳播到所有綁定的對象和元素。

2.臟值檢查(angular.js)

思路:angular.js 是通過臟值檢測的方式比對數(shù)據(jù)是否有變更,來決定是否更新視圖,最簡單的方式就是通過 setInterval() 定時(shí)輪詢檢測數(shù)據(jù)變動(dòng),angular只有在指定的事件觸發(fā)時(shí)進(jìn)入臟值檢測,大致如下:

  • DOM事件,譬如用戶輸入文本,點(diǎn)擊按鈕等。( ng-click )

  • XHR響應(yīng)事件 ( $http )

  • 瀏覽器Location變更事件 ( $location )

  • Timer事件( $timeout , $interval )

  • 執(zhí)行 $digest() 或 $apply()

3.數(shù)據(jù)劫持(Vue.js)

思路: vue.js 則是采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個(gè)屬性的setter,getter,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)。

由此可見,Object.defineProperty() 這個(gè)API是Vue實(shí)現(xiàn)雙向數(shù)據(jù)綁定的關(guān)鍵,我們先簡單了解下這個(gè)API,了解更多戳這里

二、Object.defineProperty()

簡單例子:

  var obj = {};
  Object.defineProperty(obj, 'hello', {
    get: function() {
      console.log('get val:'+ val);
      return val;
     },
    set: function(newVal) {
      val = newVal;
      console.log('set val:'+ val);
    }
  });
  obj.hello;
  obj.hello='111';

如果去掉 obj.hello=‘111' 這行代碼,則get的返回值val會(huì)報(bào)錯(cuò)val is not defined??梢奜bject.defineProperty() 監(jiān)控對數(shù)據(jù)的操作,可以自動(dòng)觸發(fā)數(shù)據(jù)同步。下面我們先用Object.defineProperty()來實(shí)現(xiàn)一個(gè)非常簡單的雙向綁定。

三、實(shí)現(xiàn)簡單的雙向綁定

 最簡單例子:


 
 
 
  
  
 
    var obj = {};   Object.defineProperty(obj, 'hello', {     get: function() {       console.log('get val:'+ val);       return val;     },     set: function(newVal) {       val = newVal;       console.log('set val:'+ val);       document.getElementById('a').value = val;       document.getElementById('b').innerHTML = val;     }   });   document.addEventListener('keyup', function(e) {    obj.hello = e.target.value;   });     

實(shí)現(xiàn)效果如下:

Vue2.0如何實(shí)現(xiàn)雙向綁定

上面例子直接用了dom操作改變了文本節(jié)點(diǎn)的值,而且是在我們知道是哪個(gè)id的情況下,通過document.getElementById 獲取到相應(yīng)的文本節(jié)點(diǎn),然后直接修改文本節(jié)點(diǎn)的值,這種做法是最簡單粗暴的。

封裝成一個(gè)框架,肯定不能是這種做法,所以我們需要一個(gè)解析dom,并能修改dom中相應(yīng)的變量的模塊。

四、實(shí)現(xiàn)簡單Compile

首先我們需要獲取文本中真實(shí)的dom節(jié)點(diǎn),然后再分析節(jié)點(diǎn)的類型,根據(jù)節(jié)點(diǎn)類型做相應(yīng)的處理。

在上面例子我們多次操作了dom節(jié)點(diǎn),為提高性能和效率,會(huì)先將所有的節(jié)點(diǎn)轉(zhuǎn)換城文檔碎片fragment進(jìn)行編譯操作,解析操作完成后,再將fragment添加到原來的真實(shí)dom節(jié)點(diǎn)中。


 
 
 
  
  {{text}}
 
    function Compile(node, vm) {    if(node) {this.$frag = this.nodeToFragment(node, vm);     return this.$frag;    }   }   Compile.prototype = {    nodeToFragment: function(node, vm) {     var self = this;     var frag = document.createDocumentFragment();     var child;     while(child = node.firstChild) {      self.compileElement(child, vm);      frag.append(child); // 將所有子節(jié)點(diǎn)添加到fragment中,child是指向元素首個(gè)子節(jié)點(diǎn)的引用。將child引用指向的對象append到父對象的末尾,原來child引用的對象就跳到了frag對象的末尾,而child就指向了本來是排在第二個(gè)的元素對象。如此循環(huán)下去,鏈接就逐個(gè)往后跳了     }     return frag;    },    compileElement: function(node, vm) {     var reg = /\{\{(.*)\}\}/;     //節(jié)點(diǎn)類型為元素     if(node.nodeType === 1) {      var attr = node.attributes;      // 解析屬性      for(var i = 0; i < attr.length; i++ ) {       if(attr[i].nodeName == 'v-model') {        var name = attr[i].nodeValue; // 獲取v-model綁定的屬性名        node.addEventListener('input', function(e) {         // 給相應(yīng)的data屬性賦值,進(jìn)而觸發(fā)該屬性的set方法          vm.data[name]= e.target.value;        });        node.value = vm.data[name]; // 將data的值賦給該node        node.removeAttribute('v-model');       }      };     }     //節(jié)點(diǎn)類型為textif(node.nodeType === 3) {      if(reg.test(node.nodeValue)) {       var name = RegExp.$1; // 獲取匹配到的字符串       name = name.trim();       node.nodeValue = vm.data[name]; // 將data的值賦給該node      }     }    },   }    function Vue(options) {    this.data = options.data;    var data = this.data;    var id = options.el;    var dom =new Compile(document.getElementById(id),this);    // 編譯完成后,將dom返回到app中    document.getElementById(id).appendChild(dom);   }   var vm = new Vue({    el: 'app',    data: {     text: 'hello world'    }   });    

到這,我們做到了獲取文本中真實(shí)的dom節(jié)點(diǎn),然后分析節(jié)點(diǎn)的類型,并能處理節(jié)點(diǎn)中相應(yīng)的變量如上面代碼中的{{text}},最后渲染到頁面中。接著我們需要和雙向綁定聯(lián)系起來,實(shí)現(xiàn){{text}}響應(yīng)式的數(shù)據(jù)綁定。

五、實(shí)現(xiàn)簡單observe

簡單的observe定義如下:

Vue2.0如何實(shí)現(xiàn)雙向綁定

需要監(jiān)控data的屬性值,這個(gè)對象的某個(gè)值賦值,就會(huì)觸發(fā)setter,這樣就能監(jiān)聽到數(shù)據(jù)變化。然后注意vm.data[name]屬性將改為vm[name]

Vue2.0如何實(shí)現(xiàn)雙向綁定

完整代碼如下:


 
 
 
  
  {{text}}
 
  function Compile(node, vm) {    if(node) {     this.$frag = this.nodeToFragment(node, vm);     return this.$frag;    }   }   Compile.prototype = {    nodeToFragment: function(node, vm) {     var self = this;     var frag = document.createDocumentFragment();     var child;     while(child = node.firstChild) {      self.compileElement(child, vm);      frag.append(child); // 將所有子節(jié)點(diǎn)添加到fragment中     }     return frag;    },    compileElement: function(node, vm) {     var reg = /\{\{(.*)\}\}/;     //節(jié)點(diǎn)類型為元素     if(node.nodeType === 1) {      var attr = node.attributes;      // 解析屬性      for(var i = 0; i < attr.length; i++ ) {       if(attr[i].nodeName == 'v-model') {        var name = attr[i].nodeValue; // 獲取v-model綁定的屬性名        node.addEventListener('input', function(e) {         // 給相應(yīng)的data屬性賦值,進(jìn)而觸發(fā)該屬性的set方法          vm[name]= e.target.value;        });        node.value = vm[name]; // 將data的值賦給該node        node.removeAttribute('v-model');       }      };     }     //節(jié)點(diǎn)類型為text     if(node.nodeType === 3) {      if(reg.test(node.nodeValue)) {       var name = RegExp.$1; // 獲取匹配到的字符串       name = name.trim();       node.nodeValue = vm[name]; // 將data的值賦給該node       // new Watcher(vm, node, name);      }     }    },   }   function defineReactive (obj, key, val) {    Object.defineProperty(obj, key, {     get: function() {      return val;     },     set: function (newVal) {      if(newVal === val) return;      val = newVal;      console.log(val);     }    })   }   function observe(obj, vm) {    Object.keys(obj).forEach(function(key) {     defineReactive(vm, key, obj[key]);    })   }    function Vue(options) {    this.data = options.data;    var data = this.data;    observe(data, this);    var id = options.el;    var dom =new Compile(document.getElementById(id),this);    // 編譯完成后,將dom返回到app中    document.getElementById(id).appendChild(dom);   }   var vm = new Vue({    el: 'app',    data: {     text: 'hello world'    }   });    

結(jié)果:

Vue2.0如何實(shí)現(xiàn)雙向綁定

到這,雖然set方法觸發(fā)了,但是文本節(jié)點(diǎn){{text}}的內(nèi)容沒有變化,要讓綁定的文本節(jié)點(diǎn)同步變化,我們需要引入訂閱發(fā)布模式。

六、訂閱發(fā)布模式

訂閱發(fā)布模式(又稱觀察者模式)定義了一種一對多的關(guān)系,讓多個(gè)觀察者同時(shí)監(jiān)聽某一個(gè)主題對象,這個(gè)主題對象的狀態(tài)發(fā)生改變時(shí)就會(huì)通知所有觀察者對象。

發(fā)布者發(fā)出通知 => 主題對象收到通知并推送給訂閱者 => 訂閱者執(zhí)行相應(yīng)操作

首先我們要一個(gè)收集訂閱者的容器,定義一個(gè)Dep作為主題對象

Vue2.0如何實(shí)現(xiàn)雙向綁定

然后定義訂閱者Watcher

Vue2.0如何實(shí)現(xiàn)雙向綁定

添加訂閱者Watcher到主題對象Dep,發(fā)布者發(fā)出通知放到屬性監(jiān)聽里面

Vue2.0如何實(shí)現(xiàn)雙向綁定

最后需要訂閱的地方

Vue2.0如何實(shí)現(xiàn)雙向綁定

至此,比較簡單地實(shí)現(xiàn)了我們第三步用dom操作實(shí)現(xiàn)的雙向綁定效果,代碼:


 
 
 
  
  {{text}}
 
 
  function Compile(node, vm) {
   if(node) {
    this.$frag = this.nodeToFragment(node, vm);
    return this.$frag;
   }
  }
  Compile.prototype = {
   nodeToFragment: function(node, vm) {
    var self = this;
    var frag = document.createDocumentFragment();
    var child;

    while(child = node.firstChild) {
     self.compileElement(child, vm);
     frag.append(child); // 將所有子節(jié)點(diǎn)添加到fragment中
    }
    return frag;
   },
   compileElement: function(node, vm) {
    var reg = /\{\{(.*)\}\}/;

    //節(jié)點(diǎn)類型為元素
    if(node.nodeType === 1) {
     var attr = node.attributes;
     // 解析屬性
     for(var i = 0; i < attr.length; i++ ) {
      if(attr[i].nodeName == 'v-model') {
       var name = attr[i].nodeValue; // 獲取v-model綁定的屬性名
       node.addEventListener('input', function(e) {
        // 給相應(yīng)的data屬性賦值,進(jìn)而觸發(fā)該屬性的set方法
         vm[name]= e.target.value;
       });
       // node.value = vm[name]; // 將data的值賦給該node
       new Watcher(vm, node, name, 'value');
      }
     };
    }
    //節(jié)點(diǎn)類型為text
    if(node.nodeType === 3) {
     if(reg.test(node.nodeValue)) {
      var name = RegExp.$1; // 獲取匹配到的字符串
      name = name.trim();
      // node.nodeValue = vm[name]; // 將data的值賦給該node
      new Watcher(vm, node, name, 'nodeValue');
     }
    }
   },
  }
  function Dep() {
   this.subs = [];
  }
  Dep.prototype = {
   addSub: function(sub) {
    this.subs.push(sub);
   },
   notify: function() {
    this.subs.forEach(function(sub) {
     sub.update();
    })
   }
  }
  function Watcher(vm, node, name, type) {
   Dep.target = this;
   this.name = name;
   this.node = node;
   this.vm = vm;
   this.type = type;
   this.update();
   Dep.target = null;
  }

  Watcher.prototype = {
   update: function() {
    this.get();
    this.node[this.type] = this.value; // 訂閱者執(zhí)行相應(yīng)操作
   },
   // 獲取data的屬性值
   get: function() {
    this.value = this.vm[this.name]; //觸發(fā)相應(yīng)屬性的get
   }
  }
  function defineReactive (obj, key, val) {
   var dep = new Dep();
   Object.defineProperty(obj, key, {
    get: function() {
      //添加訂閱者watcher到主題對象Dep
     if(Dep.target) {
      // JS的瀏覽器單線程特性,保證這個(gè)全局變量在同一時(shí)間內(nèi),只會(huì)有同一個(gè)監(jiān)聽器使用
      dep.addSub(Dep.target);
     }
     return val;
    },
    set: function (newVal) {
     if(newVal === val) return;
     val = newVal;
     console.log(val);
     // 作為發(fā)布者發(fā)出通知
     dep.notify();
    }
   })
  }
  function observe(obj, vm) {
   Object.keys(obj).forEach(function(key) {
    defineReactive(vm, key, obj[key]);
   })
  }

   function Vue(options) {
   this.data = options.data;
   var data = this.data;
   observe(data, this);
   var id = options.el;
   var dom =new Compile(document.getElementById(id),this);
   // 編譯完成后,將dom返回到app中
   document.getElementById(id).appendChild(dom);
  }
  var vm = new Vue({
   el: 'app',
   data: {
    text: 'hello world'
   }
  });
 
 

七、總結(jié)

關(guān)于雙向綁定的實(shí)現(xiàn),看了網(wǎng)上很多資料,開始看到是對Vue源碼的解析,看的過程似懂非懂。后來找到參考資料1,然后自己跟著實(shí)現(xiàn)一遍,才理解許多。感謝這篇文章的作者,寫的由淺入深,比較好理解。為了加深自己的理解,于是自己順著這個(gè)思路寫下這個(gè)筆記。本文主要了解了幾種雙向綁定的做法,然后先用原生JS,dom操作實(shí)現(xiàn)一個(gè)最簡單雙向綁定,在這個(gè)基礎(chǔ)上進(jìn)行改裝,為減少dom操作,實(shí)現(xiàn)簡單的Compile(編譯HTML);接著為了實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽,實(shí)現(xiàn)observe;最后為了實(shí)現(xiàn)數(shù)據(jù)的雙向綁定實(shí)現(xiàn)訂閱發(fā)布模式。

雖然實(shí)現(xiàn)的比較簡單,有很多功能沒有考慮,不過這個(gè)過程還是可以理解到Vue實(shí)現(xiàn)雙向綁定的原理。過程中,有思考:

1. Vue的源代碼中,用了文檔碎片fragment作為真實(shí)節(jié)點(diǎn)的存儲(chǔ)嗎?

之前有聽說用VDOM,在Vue源代碼中,也找過是否有創(chuàng)建文檔碎片,結(jié)果沒找到。看了參考資料4中,VDOM的介紹,好像是把節(jié)點(diǎn)用JS對象模擬。類似:

模板


 Item 1
 Item 2
 Item 3

js對象

var element = {
 tagName: 'ul', // 節(jié)點(diǎn)標(biāo)簽名
 props: { // DOM的屬性,用一個(gè)對象存儲(chǔ)鍵值對
  id: 'list'
 },
 children: [ // 該節(jié)點(diǎn)的子節(jié)點(diǎn)
  {tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
  {tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
  {tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
 ]
}

以上就是“Vue2.0如何實(shí)現(xiàn)雙向綁定”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


網(wǎng)站題目:Vue2.0如何實(shí)現(xiàn)雙向綁定
標(biāo)題來源:http://weahome.cn/article/pcisic.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部