前端幹活系列----vue核心源碼解讀

vue如今很是火了,好多人都在用。博主爲了提升本身的技術水平,也開始瞭解讀vue的源碼。過程很疼苦,多虧了從公衆號裏看到的一個帖子。博主也不藏私發佈出來供你們一塊兒欣賞(相關代碼在最底部): 
https://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651554000&idx=1&sn=08219fe9433fef033b17b98d1072367f&chksm=80255711b752de07e163f621ed848096850130a4484e0c81357917fd6e044a76a3cd5cf17926&scene=0#rdjavascript

提及來這解讀的也不是vue的源碼,而是這位大神的源碼。只是跟vue實現綁定的的原理同樣。而我這篇文章也是對他裏面源碼的一些解讀,但願可以讓更多的人更能理解vue。反正博主看懂了以後,對vue的做者崇拜更深了。php

vue原理的實現主要是經過 Object對象的defineProperty屬性來完成的。前端

你內心如今可能要罵爹了,就這麼一個東西就實現了雙向綁定。這不就是一個Object定義屬性的一個方法麼,不知道你是啥心情,博主反正就是這種心情。可是呢,首先你不能把vue想的太牛逼了,雖然就是很牛逼。可是當你懂了以後你知道他其實並無太複雜,這就是所謂的大道至簡吧。也只有這樣的代碼纔會讓更多的人感受很美妙不是麼。vue

vue的運行能夠經過一張圖來展現java

這裏你不用急着弄懂,等你看了博主一步步的講解以後這裏每個箭頭的含義天然而然的就明白了。node

因爲正序要好久纔會切入界面變化的實現效果,因此博主在這裏採用倒序的方式進行講解。讓你從界面上產生的疑惑一步一步的追到根。 
segmentfault

根據vue的雙向綁定原理,只要input框綁定的值跟div綁定的值是同一個,你修改input框的時候div的值也會發生改變。若是你想用原生js人爲的修改div的值找到須要修改的元素,設置一下innerHTML是否是就能夠了。你想要修改input的值只須要修改一下value的值是否是就能夠了。微信

document.getElementById("**").value="333"document.getElementById("**").innerHtml="333"

因此這裏定義了一個Watcher對象來完成這件事情相關代碼以下:svg

function Watcher(name, el, vm, exp, attr) {
  this.name = name; //指令名稱,例如文本節點,該值設爲"text"
  this.el = el; //指令對應的DOM元素
  this.vm = vm; //指令所屬myVue實例
  this.exp = exp; //指令對應的值,本例如"number"
  this.attr = attr; //綁定的屬性值,本例爲"innerHTML"

  this.update();
}

Watcher.prototype.update = function() {
  this.el[this.attr] = this.vm.$data[this.exp];
}

只要實例化一個Watcher,而後想改動頁面數據的時候調用一下Watcher的update方法是否是就實現了界面數據的更改。this

如今界面數據的更改你知道了,如今你確定想知道如何通知的Watcher的了吧。從圖中你能夠看到是Observer發送的通知對吧,而Observer又是new MVVM() 衍生過來的,因此在new MVVM()的原型鏈中確定有Observer這個方法,上文中提到的defineProperty也是在這裏定義的,不懂defineProperty的能夠參考下面這篇文章 
https://segmentfault.com/a/1190000011294519

defineProperty能夠爲添加的屬性設置set方法,當屬性修改了的時候就會執行這個方法,那咱們是否是隻須要在set方法裏面調用一下咱們定義的Watcher的update方法就能夠實現界面數據修改了呢(相關代碼在最底部)。

myVue.prototype._obverse = function(obj) {
      .......
      Object.defineProperty(_this.$data, key, {
        .....
        set: function(newVal) {
          if (value !== newVal) {
            value = newVal;
            binding._directives.forEach(function(item) {
              //item就是Watcher對象
              item.update();
            })
          }
        }
      })
    }
  })
}

這樣剩下的事情就只有一個了就是圖中的compile解析命令了。解析的就是你在模板中加入的b-click,b-model,b-bind等等命令。這裏只實現了這三個命令,其餘的命令你能夠自由實現,原理同樣。這個時候你就疑惑了咋解析啊,說解析你可能比較糊塗,我換個詞。我把解析換成判斷元素是否有這個屬性,你是否是以爲瞬間簡單了很多。判斷元素的屬性只須要用hasAttribute方法是否是就能夠了,當你知道他有這個屬性的時候,給對應的屬性添加一個事件監聽是否是就能夠了,好比input修改事件:

if (node.hasAttribute('v-model') && (node.tagName == 'INPUT' || node.tagName == 'TEXTAREA')) {
      node.addEventListener('input', (function(key) {
        var attrVal = node.getAttribute('v-model');
        _this._binding[attrVal]._directives.push(new Watcher(          'input',
          node,
          _this,
          attrVal,          'value'
        ))        return function() {
          _this.$data[attrVal] = nodes[key].value;
        }
      })(i));
    }

上文compile方法中提到的item也是在這裏定義的對象,讀到這裏你是否是整個流程灌輸起來了。有一種茅塞頓開的感受,可是你仔細琢磨就會有一種懂了,可是開口卻說不出來,由於不少細節文章中沒有提到,這些細節都比較簡單,這個思路你明白了。這些細節只是將你腦海中的那張圖描繪的更清楚的。

你可能會疑問如何監聽的全部的屬性,這裏我能夠告訴你用到的是遞歸方法。你若是不懂遞歸也能夠看博主的這篇文章 
https://blog.csdn.net/m0_37479946/article/details/79973315

代碼鏈接: 
https://pan.baidu.com/s/1lYGjz24KFau6MpY7fKYsJw 
祕鑰能夠關注博主公衆號吵吵日記回覆myVue獲取哦。 


本文分享自微信公衆號 - 點滴前端(DDIWEB)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索