前端 MVVM 道理

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

《前端 MVVM 道理》

  • Model:数据模型 & 手动衬着模板
  • View:模板
  • Controller:修正数据

MVP

《前端 MVVM 道理》

  • Model:数据模型
  • View:模板
  • Presenter:修正数据 & 手动衬着模板

MVP 是 MVC 形式的一种革新(这里不说革新,是因为二者实在很相似,没有本质上的变化),将 手动衬着 步骤** 从 Model 移到了 Presenter,让 View 和 Model 更自力更存粹了,但从另一个角度来讲,也加大了 Presenter 的事情。

MVVM

《前端 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];
    }
});

以上。

参考资料

  1. 《当代前端 手艺剖析》,张成文,2017 年 4 月第 1 版d
  2. 《MVC,MVP 和 MVVM 的图示》,阮一峰,2015年2月 1日,http://www.ruanyifeng.com/blo…
    原文作者:henry_chen
    原文地址: https://segmentfault.com/a/1190000013464776
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞