author: 陳家賓 email: 617822642@qq.com date: 2018/3/1
都說懶惰令人進步,MVVM 的進化史,正印證了這句話,是一步步讓開發人員更懶惰更簡單的歷史:html
直接 DOM 操做 -> MVC -> MVP -> MVVM
最開始的前端交互,是很直接的 DOM 操做,最出名的這類庫當數 jQuery 了,封裝了 DOM API,讓一切 DOM 操做都變得簡單。前端
但當頁面數據和交互多的時候,散亂的代碼將使項目變得難以維護,讓人發狂。因此纔有了 MV* 模式的發展。node
MVC & MVP & MVVM 三者對比僞代碼: 點我
MVP 是 MVC 模式的一種改造(這裏不說改進,是由於二者其實很類似,沒有本質上的變化),將 手動渲染 步驟** 從 Model 移到了 Presenter,讓 View 和 Model 更獨立更存粹了,但從另外一個角度來講,也加大了 Presenter 的工做。git
MVVM 模式依靠 Directive,實現了 模板自動渲染,極大地解放了開發者的雙手,此時開發者只需關注 View 和 Model,效率和可維護性方面達到了飛躍式的進步。github
下面將着重介紹下神奇的 Directive。mvc
在頁面須要改變時,手動觸發檢測,改變 model 數據,並掃描元素,對有特殊標記的元素進行修改mvvm
let data = { value: 'hello' }; let directive = { html: function (html) { this.innerHTML = html; }, value: function (html) { this.setAttribute('value', value); } }; ViewModelSet('value', 'hello world'); function ViewModelSet(key, value) { data[key] = value; scan(); } function scan() { for (let elem of elems) { elem.directive = []; for (let attr of elem.attributes) { if (attr.nodeName.indexOf('v-') >= 0) { directive[attr.nodeName.slice(2)].call(elem, data[attr.nodeValue]); } } } }
針對手動綁定進行優化,只對修改到的數據進行更新元素優化
function scan(elems, val) { let list = document.querySelectorAll(`[v-bind=${val}]`); // 只掃描修改到的數據涉及的元素 for (let elem of elems) { for (let attr of elem.attributes) { let dataKey = elem.getAttribute('v-bind'); if (elem.directive[attr.nodeValue] !== data[dataKey]) { // 當元素值有變時,更新元素 directive[attr.nodeValue].call(elem, data[dataKey]); elem.directive[attr.nodeValue] = data[dataKey]; // 保存元素當前值 } } } }
在上面的基礎更進一步,使用 Object.defineProperty 對數據進行 get & set 監聽,當數據有變時,自動執行 scan 掃描並更新元素。this
原來是在改變數據時,還要手動 scan。如今只須要直接改變數據,會自動 scan,更新元素。spa
defineGetAndSet(data, 'value'); data.value = 'hello world'; function defineGetAndSet(obj, propName) { Object.efineProperty(obj, propName, { get: function () { return this.bVal; }, set: function (newVal) { this.bVal = newVal; scan(); }, enumerable: true, configurable: true }); }
與方法三相似,換了種寫法,這裏應用了 ES6 裏的 Proxy
let data = new Proxy({ get: function (obj, key) { return obj[key]; }, set: function (obj, key, val) { obj[key] = val; scan(); return obj[key]; } });
以上。