MVVM 到底比 MVC 好在哪裡?

deepfunc發表於2022-11-23

前言

MVC 和 MVVM 是比較常用的前端設計模式,你能清楚地說出 MVVM 到底比 MVC 好在哪裡嗎?我最近了解了一下 SwiftUI 裡的 MVVM 是怎麼應用的,覺得應該記錄一下自己的想法,對這兩種模式的對比和優缺點做個總結。

MVC

先來回顧下 MVC 和 MVVM 各自的特徵。前端的 MVC 如下所示:

三個抽象角色,Model 代表模型,View 代表檢視,Controller 代表控制器。三個角色間的通訊如下:

  1. View 接收到使用者的互動請求之後,會將請求轉發給 Controller。
  2. Controller 解析使用者的請求之後,就會交給對應的 Model 去處理。
  3. Model 處理完後會通知 View。

這裡要重點關注下 MVC 中資料變化是如何響應到介面上的:Model 和 View 之間使用了觀察者模式,以便在 Model 變化時去響應介面的變化,也就是說 View 是依賴 Model 的。

MVVM

MVVM 中也有三個角色,Model 代表模型,View 代表檢視,ViewModel 代表檢視模型。通訊的步驟如下:

  1. View 把使用者請求傳送給 ViewModel。
  2. ViewModel 通知 Model 去處理。
  3. Model 處理完後通知 ViewModel 需要更新。
  4. ViewModel 通知 View 需要更新,View 從 ViewModel 中取得需要的資料。

我們應該注意到 MVVM 跟 MVC 有一些不同:View 和 Model 沒有直接的依賴關係,並且 ViewModel 會承擔一定的顯示邏輯處理。

MVVM 好在哪裡?

在 MVC 中,主要有兩個缺點:

  1. 當 Model 發生變化時 View 會收到通知以便取得需要的資料,這意味著 View 可以直接訪問 Model,也就是 View 耦合了 Model。那麼當 Model 發生變化時,所有相關的 View 都會受到影響。
  2. 因為 Model 中不包含顯示邏輯,所以不可避免地使 View 中會包含一定的業務顯示邏輯,造成 View 的職責不清和可能的程式碼重複。

以上的缺點在 MVVM 中都得到了改進。第一,View 只關聯 ViewModel,降低了耦合性;其次,View 可以關聯多個 ViewModel 來組合介面的顯示,相關聯 Model 的業務顯示邏輯程式碼可以抽離到 ViewModel 中,提高了程式碼可重用性和靈活性,並使 View 更純粹;最後,View 和 ViewModel 通常使用雙向資料繫結來簡化開發者的程式碼編寫。

MVVM 在 SwiftUI 裡怎麼用的

來看個 MVVM 的實際例子。SwiftUI 是透過繼承和屬性包裝器來實現 MVVM 的應用場景的,程式碼寫起來非常簡單:

import SwiftUI

/**
 * ViewModel 層。
 * ViewModel 是可觀察的物件,繼承自 ObservableObject,觀察者是 View。
 */
class ViewModel: ObservableObject {
  typealias Card = MemoryGame<String>.Card
  
  static private let emojis = ["?", "?", "?", "?", "?", "?", "✈️", "?"]
  
  static private func createMemoryGame() -> MemoryGame<String> {
    MemoryGame<String>(numberOfPairsOfCards: 4) { idx in
      emojis[idx]
    }
  }
  
  /**
   * ViewModel 引用了 Model。
   * model 屬性是 Model 層,注意有個 @Published 屬性包裝器。
   * @Published 的作用是當 model 發生變化時,ViewModel 釋出通知去通知觀察者,也就是 View。
   * 這裡簡化了 Model 通知 ViewModel,ViewModel 再通知 View 的過程。
   */
  @Published private var model = createMemoryGame()
  
  /**
   * 以下體現的是 ViewModel 代理了對於 Model 的訪問。
   * 一些顯示邏輯程式碼可以抽離到這裡。
   */
  var cards: Array<Card> {
    return model.cards
  }
  
  func choose(_ card: Card) {
    model.choose(card)
  }
}
import SwiftUI

struct EmojiMemoryGameView: View {
  /**
   * @ObservedObject 屬性包裝器註冊了 View 和 ViewModel 的觀察關係。
   * 當 ViewModel 發生變化時 View 會重新渲染介面。
   */
  @ObservedObject var vm: EmojiMemoryGame
  
  var body: some View {
    // 顯示的介面。
  }
}

總結

透過以上的描述應該可以清楚地回答出 MVVM 比 MVC 到底好在哪裡了。並且可以發現不同的 UI 框架在 MVVM 的使用上可能各有側重點,比如童鞋們可以把 Vue 的 MVVM 與 SwiftUI 做個對比。

相關文章