前端MVVM模式及其在Vue和React中的體現


MVVM相關概念


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層之間的映射和通信都是由框架自動完成的,

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框架自動生成在瀏覽器中。

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}等等

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對其進行更改的,會報錯,有如下三種解決方案:

  • 依然使用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實例:

    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傳遞的數據是綁定的,即父組件中該數據一旦發生變化,子組件中的也跟着變化)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM