zoukankan      html  css  js  c++  java
  • MVVM、MVC框架的认识

    推荐博客:
    https://blog.csdn.net/jia12216/article/details/55520426

    https://www.cnblogs.com/sunny_z/p/7093663.html 


    一、MVVM是Model-View-ViewModel的简写。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。当然这些事 ViewModel 已经帮我们做了,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。微软的WPF带来了新的技术体验,如Silverlight、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了 诸如BindingDependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。

    MVVM优点编辑
    MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点
    1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
    2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
    3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blending可以很容易设计界面并生成xaml代码。
    4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
    MVVM设计模式的缺点 
    第一点:数据绑定使得 Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。 
    第二点:一个大的模块中,model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存,就造成了花费更多的内存。 
    第三点:数据双向绑定不利于代码重用。客户端开发最常用的重用是View,但是数据双向绑定技术,让你在一个View都绑定了一个model,不同模块的model都不同。那就不能简单重用View了。 
     

    MVVM框架的主要应用场景

      1)针对具有复杂交互逻辑的前端应用
      2)提供基础的架构抽象
      3)通过Ajax数据持久化,保证前端用户体验
      好处就是当前后端进行一些数据交互的时候,前端可以通过Ajax请求对后端做数据持久化,不需要刷新整个页面,只需要改动DOM里需要改动的那部分数据和内容,特别是对于移动端应用场景,刷新页面的代价太昂贵,会重新加载很多资源,虽然有些资源会被缓存,但是页面的DOM、JS、CSS都会被浏览器重新解析一遍,因此,移动端页面经常会做成SPA单页应用,在这个基础上就诞生了很多MVVM框架,如Angular、React、Vue

    二、MVC(Model View Controller)架构开发,它是苹果推荐的开发模式,它把页面分成三部分:数据模型页面视图页面控制器。一个页面被分成多个小视图,一个页面共享一个数据模型,只是这个数据模型只被控制器操作,不被各个子视图处理。这个架构看起来整洁多了,控制器的复杂度降低的很多,页面的显示单元被分配到各个视图去显示,控制器专注与数据的加工与处理,部分解放了控制器,使它的功能更集中,更紧凑,更小。数据模型,页面视图,页面控制器各个独立,各司其职。这就是重量级视图控制的特点。它看起来简单,更容易理解,所有的逻辑在视图控制器里处理。

    一个页面一般分三个文件夹:页面文件夹(存放页面控制器和其子文件夹),页面文件夹下的model文件夹,页面文件夹下的view文件夹。想找页面间的逻辑就去页面控制器里找,想看页面显示元素就在view文件夹下寻找。你发现数据文件很小,只是数据存储和转换,这符合数据单一性原则;视图部分也很小,只是数据的显示,完全不自主;控制器部分仍然摆脱不了过于庞大的弊病。他们就像一个人一样头重而且庞大,脚轻,身子小。看到这些你就知道这种开发架构需要进化的方向:强化控制器的重要性,减轻它的体积,把不属于它的非核心部分迁移到类似视图的那一部分去,在保证数据的定义性上增加它的功能和作用。

    取消使用单例组装http请求的做法(单例像类一样常驻内存,无形中增加类内存的开销。虽然它做到了统一管理http请求的作用,但是不符合那个页面的请求那个页面管理请求的原则)。让从控制器分化出来的这一部分功能给View,http请求封装为一个新操作类。  

    优点: 
    1. 可定制性 
    2. 代码清晰,便于维护 
    3. 测试友好性 
    4. 轻量级 
    5. 开源 
    主要缺点有两个: 
    1. View对Model的依赖,会导致View也包含了业务逻辑; 
    2. Controller会变得很厚很复杂。

    前端mvc框架,如angularjs,backbone: 

    三、MVP:Model-View-Presenter,MVC的一个演变模式,将Controller换成了Presenter,主要为了解决上述第一个缺点,将View和Model解耦 


    四、了解一下Vue双数据绑定原理

    vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。

    具体步骤:

     

    第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 settergetter

    这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化

     

    第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

     

    第三步:Watcher订阅者是ObserverCompile之间通信的桥梁,主要做的事情是:

    1、在自身实例化时往属性订阅器(dep)里面添加自己

    2、自身必须有一个update()方法

    3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。

     

    第四步:MVVM作为数据绑定的入口,整合ObserverCompileWatcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起ObserverCompile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
     

    五、什么是数据劫持:

      首先我们应该搞清楚什么是数据劫持,说白了就是通过Object.defineProperty()来劫持对象属性的setter和getter操作,在数据变动时做你想要做的事情,举个栗子:


    var data = {
    name:'lhl'
    }

    Object.keys(data).forEach(function(key){
    Object.defineProperty(data,key,{
    enumerable:true,
    configurable:true,
    get:function(){
    console.log('get');
    },
    set:function(){
    console.log('监听到数据发生了变化');
    }
    })
    });
    data.name //控制台会打印出 “get”
    data.name = 'hxx' //控制台会打印出 "监听到数据发生了变化"
    上面的这个栗子可以看出,我们完全可以控制对象属性的设置和读取。在Vue中,作者在很多地方都非常巧妙的运用了defineProperty这个方法,具体用在哪里并且它又解决了哪些问题,下面做详细的介绍:
    监听对象属性的变化
    这个应该是Vue非常重要的一块,其主要思想是observer每个对象的属性,添加到订阅器dep中,当数据发生变化的时候发出notice通知。 相关源代码如下:(作者采用的是ES6+flow写的,代码在src/core/observer/index.js模块里面)
    export function defineReactive (
    obj: Object,
    key: string,
    val: any,
    customSetter?: Function
    ) {
    const dep = new Dep()//创建订阅对象

    const property = Object.getOwnPropertyDescriptor(obj, key)
    if (property && property.configurable === false) {
    return
    }

    // cater for pre-defined getter/setters
    const getter = property && property.get
    const setter = property && property.set

    let childOb = observe(val)//创建一个观察者对象
    Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
    const value = getter ? getter.call(obj) : val
    //这里也是作者一个巧妙设计,在创建watcher实例的时候,通过调用对象的get方法往订阅器 dep上添加这个创建的watcher实例
    if (Dep.target) {
    dep.depend()
    if (childOb) {
    childOb.dep.depend()
    }
    if (Array.isArray(value)) {
    dependArray(value)
    }
    }
    return value
    },
    set: function reactiveSetter (newVal) {
    const value = getter ? getter.call(obj) : val
    if (newVal === value) {
    return
    }
    if (process.env.NODE_ENV !== 'production' && customSetter) {
    customSetter()
    }
    if (setter) {
    setter.call(obj, newVal)
    } else {
    val = newVal
    }
    childOb = observe(newVal)//继续监听新的属性值
    dep.notify()//这个是真正劫持的目的,要对订阅者发通知了
    }
    })
    }
    以上是监听对象属性的变化,那么下面再看看如何监听数组的变化:

    监听数组的变化 

    const arrayProto = Array.prototype
    export const arrayMethods = Object.create(arrayProto)

    ;[
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
    ]
    .forEach(function (method) {
    // cache original method
    const original = arrayProto[method]
    def(arrayMethods, method, function mutator () {
    // avoid leaking arguments:
    // http://jsperf.com/closure-with-arguments
    let i = arguments.length
    const args = new Array(i)
    while (i--) {
    args[i] = arguments[i]
    }
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
    case 'push':
    inserted = args
    break
    case 'unshift':
    inserted = args
    break
    case 'splice':
    inserted = args.slice(2)
    break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
    })
    })

    ...
    /**
    * Define a property.
    */
    function def (obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
    });
    }
    通过上面的代码可以看出Vue是通过修改了数组的几个操作的原型来实现的。

    原文自: https://www.cnblogs.com/haohaoday/p/6375149.html

    Vue框架很好的利用了Object.defineProperty()这个方法来实现了数据的监听和修改,同时也达到了很好的模块间解耦,在日常开发用好这个方法说不定会达到令人意想不到的结果。 

    发布订阅者模式:

    https://www.sohu.com/a/207062452_464084 

  • 相关阅读:
    合并指定表格指定列的相同文本的相邻单元格
    [转载]>/dev/null 2>&1 含义
    有关cron
    jQuery版本对checkbox影响
    c# 如何获取项目的根目录
    Javascript 字符串组装用函数 format
    sql server 删除数据库
    说说接口封装
    有开放的接口!!!!
    支付宝支付功能的集成
  • 原文地址:https://www.cnblogs.com/yunshangwuyou/p/9602158.html
Copyright © 2011-2022 走看看