做者:刀哥(朱建)前端
前言:mvvm模式即model-view-viewmodel模式簡稱,單項/雙向數據綁定的實現,讓前端開發者們從繁雜的dom事件中解脫出來,很方便的處理數據和ui之間的聯動。本文將從vue的雙向數據綁定入手,剖析mvvm庫設計的核心代碼與思路。vue
一、需求整理與分析node
需求:git
分析:github
二、實現思路正則表達式
三、模塊劃分數組
MVVM目前劃分爲observer,compile,directive,watcher四個模塊。閉包
四、數據監聽模塊observerdom
經過es5規範中的object.defineProperty方式實現對數據的監聽。mvvm
五、實現思路
遞歸遍歷data,將data下面全部屬性都加上set,get方法,以實現對全部屬性的攔截.
注意:對象可能含有數組屬性,數組的內置有push,pop,splice等方法改變內部數據.
此時作法是改變數組的原型鏈,在原型鏈中增長一層自定義的push,pop,splice方法作攔截,這些方法裏面加上咱們本身的回調函數,而後在調用原生的push,pop,splice等方法。
export function defineProperty(obj, prop, val) { if (prop == '__observe__') { return; } val = val || obj[prop]; var dep = new Dep(); obj.__observe__ = dep; var childDep = addObserve(val); Object.defineProperty(obj, prop, { get: function() { var target = Dep.target; if (target) { dep.addSub(target); if (childDep) { childDep.addSub(target); } } return val; }, set: function(newVal) { if(newVal!=val){ val = newVal; dep.notify(); } } }); }
六、編譯模塊compiler
實現思路:
var complieTemplate = function (nodes, model) { if ((nodes.nodeType == 1 || nodes.nodeType == 11) && !isScript(nodes)) { paserNode(model, nodes); if (nodes.hasChildNodes()) { nodes.childNodes.forEach(node=> { complieTemplate(node, model); }) } } };
七、指令模塊directive
指令信息如:v-text,v-for,v-model等。
每種指令信息須要的初始化動做以及指令的刷新函數update均可能不同,因此咱們把它抽象出來單獨作一個模塊。固然也有公用的如公共屬性,統一的watcher實例化,unbind.
update函數則具體定義所屬指令如何渲染ui,如簡單的vtext指令的update函數以下:
vt.update = function (textContent) { this.el.textContent = textContent; };
九、結構圖
)
九、數據訂閱模塊watcher
watcher的功能是讓directive和observer模塊關聯起來。初始化的時候作兩件事:
因爲在defineProperty函數中定義的dep變量在setter和getter函數裏有引用,使dep變量處於閉包狀態沒有釋放,此時在getter方法中經過判斷Depend.target的存在,來獲取訂閱者watcher,經過發佈者dep儲存起來。數據的每一個屬性都有一個惟一的的dep變量,記錄着全部訂閱者watcher的信息,一旦屬性有變化,調用setter函數的時候觸發dep.notify(),通知全部已訂閱的watcher,進而執行全部與該屬性關聯的刷新函數,最後更新指定的ui。
watcher 初始化部分代碼:
Depend.target = this; this.value = this.getValue(); Depend.target = null;
observer.js 屬性定義代碼:
export function defineProperty(obj, prop, val) { if (prop == '__observe__') { return; } val = val || obj[prop]; var dep = new Dep(); obj.__observe__ = dep; var childDep = addObserve(val); Object.defineProperty(obj, prop, { get: function() { var target = Dep.target; if (target) { dep.addSub(target); if (childDep) { childDep.addSub(target); } } return val; }, set: function(newVal) { if(newVal!=val){ val = newVal; dep.notify(); } } }); }
十、流程圖
十一、總結
文基本對mvvm庫的需求整理,拆分,以及對拆分模塊的逐一實現來達到總體雙向綁定功能的實現,固然目前市場上的mvvm庫功能毫不止於此,本文只是略舉我的認爲的核心代碼。若是思路和實現上的問題,也請各位斧正,謝謝閱讀!
原代碼:https://github.com/laughing-p...
想要深刻了解的同窗能夠訪問數瀾社區,和你們一塊兒討論學習~