美文网首页
MVVM架构模式

MVVM架构模式

作者: 木中木 | 来源:发表于2019-10-06 07:17 被阅读0次

我们比较耳熟能详的mvc模式,用户action触发view,在通知到control,control再修改到model,model收到修改数据,通知到control,再反馈到view上。
mvvm即model view viewModel
model是数据,view是视图,viewmodel是视图模型,model改变能够反馈到view上,实现方式是数据绑定,view动作能够监听并反馈到model上,实现方式是dom事件监听。那上述两种方式都实现就是双向数据绑定,比如vue,react通过state改变能够触发view更新,实现单向数据绑定。
下面我们来总结一下,在mvvm模式下视图模型是不能直接通信的,通过viewmodel连接起来,通常情况下会通过对象劫持方式实现model改变,触发视图修改,通过节点事件监听方式实现视图修改同时修改model。

对象劫持(观察者-订阅)
普通的data对象传入双向数据绑定架构中比如vue,vue将data中对象遍历并通过object.defineproperties,转换为setter和getter方法。当访问data属性时会触发getter方法,当修改data属性时会触发setter方法。

vue的数据双向绑定是通过数据劫持和发布-订阅者功能来实现的
实现步骤:1.实现一个监听者Oberver来劫持并监听所有的属性,一旦有属性发生变化就通知订阅者
2.实现一个订阅者watcher来接受属性变化的通知并执行相应的方法,从而更新视图
3.实现一个解析器compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相对应的订阅

下面通过一个例子说明下

(function(){
    const data = {
        display: '',
    }
    Object.defineProperties(data, {
        display: {
            set: function(v) {
                console.log('set display value')
                // 事件分发
                publishDisplay(v);
            }
        }
    })
    document.querySelector('#bindingInput').addEventListener('keyup', function(e) {
        data.display = this.value;
    });
    function publishDisplay(v) {
        document.querySelector('#bindingDisplay').innerHTML = v;
    }
})();
或者使用es6 proxy对象完成
(function(){
    const data = {
        display: '',
    }
    const handler = {
        get: function(target, key, proxy){
            console.log('get display value');
            return target[key];
        },
        set: function(target, key, value, proxy) {
            console.log('set display value');
            target[key] = value;
            publishDisplay();
        }
    }
    const proxy = new Proxy(data, handler);

    document.querySelector('#bindingInputES6').addEventListener('keyup', function(e) {
        proxy.display = this.value;
    });
    function publishDisplay(v) {
        document.querySelector('#bindingDisplayES6').innerHTML = proxy.display;
    }
})();

下面讲述MVVM中虚拟节点——visual dom
在前面文章中,我们讲过操作节点是非常耗费性能的,频繁的操作节点导致用户的体验性非常差,所以我们有各种各样的优化措施,下面要讲述的visual dom也是其中一种,而且这种方式效率更高。
首先,我们要知道,网页上的元素和层级关系,可以通过树形的结构展示出来,比如:

const element = {
        tagName:'body',
        props:{
            style:{
                width: '200px',
            }
        },
        children: [
            {
                tagName:'div',
                props:{
                    height: '100%'
                },
                children :[{...}]
            }
        ]
    }

这种js展示出来的方式非常简洁,方便,而且效率特别高,关于效率这个,我们可以对比一下document.createElement这个方法,这里面做了很多事情,包括节点原型、事件绑定等等。所以效率都是相对而言,是非常高。
react在初始化的时候,会建立一个基于js树形结构的虚拟节点树,之后一旦修改数据,频发操作节点的时候,react在每次更新节点的时候,会新建一颗虚拟节点树,然后对新旧节点树进行diff比较,把差异部分找出来,然后交给render方法,渲染到实际的页面上去。
diff算法:传统的diff算法是O(n^3),这个显然满足不了当前的效率要求,所以react对diff进行优化,只做同层比较,也就是说,只对两棵树的同级进行比较,比较类型,如果是字符串,则直接比较是否全等,否则如果是标签,先比较标签名称,然后是树形比较,然后在递归遍历子级节点,如果节点名称或者key都不相同,则直接替换本级以及下级节点。

function dfsWalk (oldNode, newNode, index, patches) {
  var currentPatch = []

  // Node is removed.
  if (newNode === null) {
    // Real DOM node will be removed when perform reordering, so has no needs to do anthings in here
  // TextNode content replacing
  } else if (_.isString(oldNode) && _.isString(newNode)) {
    if (newNode !== oldNode) {
      currentPatch.push({ type: patch.TEXT, content: newNode })
    }
  // Nodes are the same, diff old node's props and children
  } else if (
      oldNode.tagName === newNode.tagName &&
      oldNode.key === newNode.key
    ) {
    // Diff props
    var propsPatches = diffProps(oldNode, newNode)
    if (propsPatches) {
      currentPatch.push({ type: patch.PROPS, props: propsPatches })
    }
    // Diff children. If the node has a `ignore` property, do not diff children
    if (!isIgnoreChildren(newNode)) {
      diffChildren(
        oldNode.children,
        newNode.children,
        index,
        patches,
        currentPatch
      )
    }
  // Nodes are not the same, replace the old node with new node
  } else {
    currentPatch.push({ type: patch.REPLACE, node: newNode })
  }

相关文章

网友评论

      本文标题:MVVM架构模式

      本文链接:https://www.haomeiwen.com/subject/nvuepctx.html