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]; } });
以上。