前端MVVM模式及其在Vue和React中的体现

MVVM相关概念


1) MVVM典型特色是有四个概念:Model、View、ViewModel、绑定器。MVVM能够是单向绑定也能够是双向绑定甚至是不绑定
2) 绑定器:声明性的数据和命令,存在于ViewModel之中,让ViewModel和Model两者进行自动或手动通讯,接下来的“MVVM在React中对应关系”小节有举例说明。
3) MVVM本质上是M- V-C-VM,它是在MVC的基础上增长了一层VM,只不过C变弱了,被并入到M概念中,VM用于分离V和M,而且让用户避免因为直接操做V层的DOM而带来的繁琐和效率低下,MVVM使开发更高效,结构更清晰,增长代码的复用性。
4) 在不一样的GUI(图形用户界面)上进行展现时,Model、Controller、View-Model可以复用,只需把View层进行替换。
5) 在不一样类型的UI(用户界面)上进行展现时,Model、Controller可以复用,只需把View-Model、View层进行替换。好比:假设咱们开发的是一款针对盲人的应用,那么输出设备或许咱们须要考虑使用扬声器来代替显示器,输入设备使用麦克风,这时咱们只需将上述的View-Model替换为Audio-Model做为语音模型,将 V(iew)层替换为Audio层用于播放语音和接收语音输入。
6) 我的认为:在基于MVVM框架的项目中,不论是双向数据绑定仍是单向数据绑定,你在开发中实际要面对的都是ViewModel和M(odel)层以前的通讯,由于V(iew) 和ViewModel层之间的映射和通讯都是由框架自动完成的,vue

MVVM四层结构


1) M(odel)层:模型,定义数据结构。
2) C(ontroller)层:实现业务逻辑,数据的增删改查。在MVVM模式中通常把C层算在M层中,(只有在理想的双向绑定模式下,Controller 才会彻底的消失。这种理想状态通常不存在)
3) ViewModel层:顾名思义是视图View的模型、映射和显示逻辑(如if for等,非业务逻辑),另外绑定器也在此层。ViewModel是基于视图开发的一套模型,若是你的应用是给盲人用的,那么也能够开发一套基于Audio的模型AudioModel。
4) V(iew)层:将ViewModel经过特定的GUI展现出来,并在GUI控件上绑定视图交互事件,V(iew)通常由MVVM框架自动生成在浏览器中。react

MVVM在React中对应关系


1) M(odel):对应组件的方法或生命周期函数中实现的业务逻辑和this.state中保存的本地数据,若是React集成了redux +react-redux,那么组件中的业务逻辑和本地数据能够彻底被解耦出来单独存放当作M层,如业务逻辑放在Reducer和Action中。
2) V(iew)-M(odel):对应组件中的JSX,它实质上是Virtual DOM的语法糖。React负责维护 Virtual DOM以及对其进行diff运算,而React-dom 会把Virtual DOM渲染成浏览器中的真实DOM
3) View:对应框架在浏览器中基于虚拟DOM生成的真实DOM(并不须要咱们本身书写)以及咱们书写的CSS
4)绑定器:对应JSX中的命令以及绑定的数据,如className={ this.props.xxx }、{this.props.xxx}等等redux

MVVM的双绑和单绑区别


1) 通常,只有UI表单控件才存在双向数据绑定,非UI表单控件只有单向数据绑定。
2) 单向数据绑定是指:M的变化能够自动更新到ViewModel,但ViewModel的变化须要手动更新到M(经过给表单控件设置事件监听)
3) 双向数据绑定是指念:M的变化能够自动更新到ViewModel,ViewModel的变化也能够自动更新到M
4) 双向绑定 = 单向绑定 + UI事件监听。双向和单向只不过是框架封装程度上的差别,本质上二者是能够相互转换的。
5) 优缺点:在表单交互较多的状况下,单向数据绑定的优势是数据更易于跟踪管理和维护,缺点是代码量较多比较啰嗦,双向数据绑定的优缺点和单向绑定正好相反。浏览器

三大框架的异同


1) 三大框架都是数据驱动型的框架
2) vue及angular是双向数据绑定;react是单向数据绑定。React貌似使用的也是Object.defineProperty监控数据,只是没有进一步把表单控件的事件封装进v-model
3) Vuex、Redux都是单项数据绑定的,即M的变化能够自动更新到V,但V的变化必须手动触发事件更新到M,这种单项数据绑定使数据更易于跟踪管理和维护。
4) 未完待续……数据结构

Vue双向绑定原理


1) Vue的双向数据绑定是经过Object.defineProperty的get/set对M层数据进行监控,当数据发生变化时,自动更新VM层绑定的数据,而当用户更改了VM层表单控件的数据时,经过v-model自动更新到M层(v-model是对表单控件的事件的封装)
精简示例:框架

<div id="demo"></div>
<input type="text" id="inp">
<script>
  var obj = {}
  var demo = document.querySelector('#demo')
  var inp = document.querySelector('#inp')
  Object.defineProperty(obj, 'name', { // 看起来数据name只是做为数据中转的做用,真正要更新到view层的数据操做在set方法里。
    get: function() {
      return 0
    },
    set: function (newVal) {
      inp.value = newVal
      demo.innerHTML = newVal
    }
  })
  inp.addEventListener('input', function(e) {
    obj.name = e.target.value  // 给obj的name属性赋值,进而触发该属性的set方法,
  })
  obj.name = 'fei'// 在给obj设置name属性的时候,触发了set这个方法
</script>

2)咱们已经知道Vue是双向数据绑定(经过v-model),Vuex是单向数据绑定,那么问题来了,在基于Vue+ Vuex的项目中,Vuex中的数据是不容许Vue的v-model对其进行更改的,会报错,有以下三种解决方案:dom

  • 依然使用v-model,数据不放进Vuex中,而是放在组件自身的data属性中
  • 依然使用v-model,不过取值再也不是Vuex中的数据,而是组件自身的一个computed(getter/setter)或watch,经过computed或watch里的回调来把数据变化提交(commit)到Vuex
    组件模板:函数

    <template>
      <div>
         <input  type="text" v-model="newName"/>
         <p>{{newName}}</p>
      </div>
    </template>

    组件VUE实例:this

    computed: {
        newName: {
          get () {
            return this.$store.state.name
          },
          set (val) {
            this.$store.commit('changeName', val) //当newName 值发生改变时,提交一个mutation:changeName,用于改变store中的name/
          }
        }
      }

    mutation:双向绑定

    changeName (state, val) {
       state.name = val
     }
  • 不使用v-model,经过UI事件监听触发一个回调,而后手动把数据变化提交(commit)到Vuex

3)Vue的双向数据绑定和Vue的prop的单项数据流是两个不一样的概念,数据绑定的前提是有数据流,但有数据流不必定有数据绑定。prop的单项数据流是指:prop能够把父组件的数据传递给子组件而且子组件不能对该数据进行直接修改更不能回流到父组件(固然,得益于Vue对全部数据使用了Object.defineProperty,因此prop传递的数据是绑定的,即父组件中该数据一旦发生变化,子组件中的也跟着变化)

相关文章
相关标签/搜索