author: 陈家宾
email: 617822642@qq.com
date: 2018/3/1
MVVM 背景
都说懒散使人提高,MVVM 的进化史,正印证了这句话,是一步步让开发人员更懒散更简朴的汗青:
直接 DOM 操纵 -> MVC -> MVP -> MVVM
最最先的前端交互,是很直接的 DOM 操纵,最着名的这类库当数 jQuery 了,封装了 DOM API,让统统 DOM 操纵都变得简朴。
但当页面数据和交互多的时刻,狼藉的代码将使项目变得难以保护,让人发疯。所以才有了 MV* 形式的生长。
MV* 形式
MVC & MVP & MVVM 三者对照伪代码:
点我
MVC
- Model:数据模型 & 手动衬着模板
- View:模板
- Controller:修正数据
MVP
- Model:数据模型
- View:模板
- Presenter:修正数据 & 手动衬着模板
MVP 是 MVC 形式的一种革新(这里不说革新,是因为二者实在很相似,没有本质上的变化),将 手动衬着 步骤** 从 Model 移到了 Presenter,让 View 和 Model 更自力更存粹了,但从另一个角度来讲,也加大了 Presenter 的事情。
MVVM
- Model:数据模型
- View:带特别属性的 html 模板
- ViewModel:依托 Directive,修正数据 & 自动衬着
MVVM 形式依托 Directive,完成了 模板自动衬着,极大地解放了开发者的双手,此时开发者只需关注 View 和 Model,效力和可保护性方面达到了奔腾式的提高。
下面将偏重引见下奇异的 Directive。
数据变动检测计划(Directive)
(一)手动触发绑定
在页面须要转变时,手动触发检测,转变 model 数据,并扫描元素,对有特别标记的元素举行修正
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]; // 保留元素当前值
}
}
}
}
(三)前端数据对象挟制(Hijacking)
在上面的基本更进一步,运用 Object.defineProperty 对数据举行 get & set 监听,当数据有变时,自动实行 scan 扫描并更新元素。
原来是在转变数据时,还要手动 scan。如今只须要直接转变数据,会自动 scan,更新元素。
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
});
}
(四)ECMAScript 6 Proxy
与要领三相似,换了种写法,这里应用了 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];
}
});
以上。
参考资料
- 《当代前端 手艺剖析》,张成文,2017 年 4 月第 1 版d
- 《MVC,MVP 和 MVVM 的图示》,阮一峰,2015年2月 1日,http://www.ruanyifeng.com/blo…