Swift+Redux

在聊Redux之前,我們先回顧一下之前我們使用過的設(shè)計(jì)模式MVC,MVVM,MVP,VIPER。
如圖所示,由于這些設(shè)計(jì)模式是基于數(shù)據(jù)的流轉(zhuǎn)來定義,所以我們也可以把他們統(tǒng)稱為數(shù)據(jù)流框架。



用了這么多年的類MVC設(shè)計(jì)模式,我們發(fā)現(xiàn)了一些缺陷。

  • Controller容易臃腫,尤其當(dāng)業(yè)務(wù)復(fù)雜的時(shí)候
  • 耦合度高,測試性差。各層互相依賴,一個(gè)view依賴多個(gè)model,一個(gè)model 也會(huì)依賴多個(gè)view,一旦出現(xiàn)了bug,追蹤困難,上下文易丟失。
  • 復(fù)用差

通過上邊問題的描述,我們反過來看怎么解決這個(gè)問題,所以我們希望的理想設(shè)計(jì)模式是什么樣的

  • 各層依賴單一
  • 耦合度低,上下文明確
  • 復(fù)用強(qiáng)

試想如果我們把類MVC的這種數(shù)據(jù)的流轉(zhuǎn)模式,變成單向的,是不是就解決了這個(gè)問題。
為了解決這個(gè)問題,React里出現(xiàn)了一個(gè)有名的框架。

Redux

下面聊聊redux怎么解決這個(gè)問題。


構(gòu)成
  • Store 包含三部分
    Dispatch: 分發(fā)Action的方法
    Reducer:去處理Action,更新store
    State:狀態(tài)
  • Action:描述了行為的數(shù)據(jù)結(jié)構(gòu)。使用action描述所有變化帶來的好處是可以清晰的知道應(yīng)用發(fā)生了什么,如果應(yīng)用發(fā)生了變化,就知道為什么這么變,action就像發(fā)生變化的指示器
  • Reducer:跟swift的函數(shù)reduce的思想類似,新狀態(tài)是由前一個(gè)狀態(tài)累加起來。
    Action通過reducer觸發(fā)store的更新。
    對(duì)于大應(yīng)用來說,不可能僅僅寫一個(gè)這樣的函數(shù),可以編寫多個(gè)小函數(shù)分別管理state的一部分。
  • State: state只讀的,唯一改變state的方式就是觸發(fā)action

點(diǎn)擊view觸發(fā)一個(gè)action,通過dispatcher dispatch到reducer中處理,生成一個(gè)新的state,觸發(fā)store的更新,通知到view的更新

三個(gè)特性
  1. 單數(shù)據(jù)流模式
    所有狀態(tài)存放在唯一的Store中,View 內(nèi)部也是盡量沒有自己的狀態(tài),當(dāng)Store中狀態(tài)變化,則View會(huì)進(jìn)行更新,當(dāng)View有用戶的操作,則Store就進(jìn)行更新。 所有狀態(tài)清晰明了,當(dāng)發(fā)現(xiàn)問題時(shí)候只需要檢查狀態(tài)就可以了。
  2. 可以預(yù)測性,(通過reducer的含義)
    state + action = new state
    state發(fā)生的任何變化,一定是有action引起的,保證了Redux應(yīng)用一定是會(huì)被追蹤的,一旦發(fā)現(xiàn)狀態(tài)發(fā)生了問題,一定是能找到對(duì)應(yīng)的action
  3. 純函數(shù)更新Store
    純函數(shù)的定義:函數(shù)的輸出結(jié)果完全取決于傳進(jìn)來的參數(shù),不依賴任何外部變量,不會(huì)產(chǎn)生副作用
    特點(diǎn):容易測試

通過Redux的三個(gè)特性,對(duì)應(yīng)了上邊提到的解決了mvc架構(gòu)的痛點(diǎn)

Redux Demo

基于上邊的概念,我們用代碼實(shí)現(xiàn)一下單數(shù)據(jù)流
功能上簡單實(shí)現(xiàn)一個(gè)列表

  • Action 描述具體的行為
enum MainPageAction {
  case onClickRefresh
  case refresh(list: [Classification])
}
  • Store 接受view的發(fā)出的Action
class MainPageStore: ObservableObject {
  
  private let reducer: Reducer
  @Published public var state: MainPageState

  init(state: MainPageState, reducer: @escaping Reducer) {
    self.state = state
    self.reducer = reducer
  }
  
  func dispatch(action: MainPageAction) {
    if Thread.current.isMainThread {
      self.state = self.reducer(action, state)
    } else {
      DispatchQueue.main.sync {
        self.state = self.reducer(action, state)
      }
    }
  }
}

extension MainPageState {
  func classificationList() {
    Task {
      let list = await Classification.classifications()
      mainPageStore.dispatch(action: .refresh(list: list))
    }
  }
}
  • Reducer 處理Action
typealias Reducer = (MainPageAction, MainPageState) -> MainPageState

func mainPageReducer(action: MainPageAction, state: MainPageState) -> MainPageState {
  var state = state
  switch action {
  case .onClickRefresh:
    state.classificationList()
  case .refresh(let list):
    state.list = list
  }
  return state
}
  • State 提供數(shù)據(jù),描述頁面狀態(tài)
struct MainPageState {
  var list: [Classification] = []
}
  • view SwiftUI展示并監(jiān)聽數(shù)據(jù)變化
let mainPageStore = MainPageStore(state: MainPageState(), reducer: mainPageReducer)

struct MainView: View {
  @EnvironmentObject var store: MainPageStore
  var body: some View {
    NavigationView {
      List {
        ForEach(store.state.list) { item in
          NavigationLink {
            ClassificationList(item: item)
          } label: {
            MainPageCell(item: item)
          }
        }
      }.navigationTitle(Text("Bookmarks"))
        .navigationBarTitleDisplayMode(.automatic)
        .toolbar {
          Image(systemName: "arrow.clockwise").onTapGesture {
            store.dispatch(action: .onClickRefresh)
          }
        }
    }.onAppear {
      store.dispatch(action: .onClickRefresh)
    }
  }
}

把上邊的代碼進(jìn)行優(yōu)化,抽象出Action協(xié)議,protocol Action {},Reducer,Store增加異步處理,參考框架ReSwift

https://github.com/lifexg/Blog
Blog應(yīng)用主要采用了Redux + SwiftUI + iCloudKit 技術(shù),可在APP Store搜書簽

功能點(diǎn)

  • 書簽列表
  • iCloud存儲(chǔ)同步數(shù)據(jù)
  • Widget
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 軟件架構(gòu)是指,設(shè)計(jì)軟件的人為軟件賦予的形狀,這個(gè)形狀是指系統(tǒng)如何被劃分為組件(Components),各個(gè)組件如何...
    沈楓_ShenF閱讀 1,994評(píng)論 0 12
  • 前段時(shí)間在RxSwift上做了一些實(shí)踐,Rx確實(shí)是一個(gè)強(qiáng)大的工具,但同時(shí)也是一把雙刃劍,如果濫用的話反而會(huì)帶來副作...
    L_Zephyr閱讀 4,299評(píng)論 0 15
  • Flux, Reflux和Redux它們都是用于管理數(shù)據(jù)流轉(zhuǎn)的。那么它們?yōu)槭裁磿?huì)出現(xiàn),各自又解決了什么問題呢? F...
    JellyL閱讀 1,644評(píng)論 0 1
  • Actions Actions是用于存放數(shù)據(jù)的載體,通過store.dispatch()函數(shù)來將數(shù)據(jù)從app發(fā)送到...
    放風(fēng)箏的小小馬閱讀 878評(píng)論 0 1
  • 首先聲明這一篇完全是根據(jù)同事的視頻學(xué)習(xí)的筆記哈~~ MVC 由于其實(shí)MVVM、MVP都是基于MVC,所以先看MVC...
    木小易Ying閱讀 3,697評(píng)論 4 6

友情鏈接更多精彩內(nèi)容