天天看點

即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

作者:閑魚技術-吉豐

背景

在閑魚深度使用 Flutter 開發過程中,我們遇到了業務代碼耦合嚴重,代碼可維護性糟糕,如入泥濘。對于閑魚這樣的負責業務場景,我們需要一個統一的應用架構來擺脫當下的開發困境,而這也是 Flutter 領域空缺的一塊處女地。

Fish Redux 是為解決上面問題上層應用架構,它是一個基于 Redux 資料管理的組裝式 flutter 應用架構, 特别适用于建構中大型的複雜應用。

它的最大特點是配置式組裝, 一方面将一個大的頁面,對視圖和資料層層拆解為互相獨立的 Component|Adapter,上層負責組裝,下層負責實作,另一方面将 Component|Adapter 拆分為 View,Reducer,Effect 等互相獨立的上下文無關函數。是以它會非常幹淨,易編寫、易維護、易協作。

Fish Redux 的靈感主要來自于 Redux、React、Elm、Dva 這樣的優秀架構,而 Fish Redux 站在巨人的肩膀上,将集中,分治,複用,隔離做的更進一步。

分層架構圖

即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

架構圖,主體自底而上,分三層,每一層用來解決不通層面的問題和沖突,下面依次來展開。

Redux

  • Redux 是來自前端社群的一個資料管理架構, 對 Native 開發同學來說可能會有一點陌生,我們做一個簡單的介紹。

Redux 做什麼的?

  • Redux 是一個用來做可預測易調試的資料管理的架構。所有對資料的增删改查等操作都由 Redux 來集中負責。

Redux 是怎麼設計和實作的?

  • Redux 是一個函數式的資料管理的架構。
    傳統 OOP 做資料管理,往往是定義一些 Bean,每一個 Bean 對外暴露一些 Public-API 用來操作内部資料(充血模型)。
    函數式的做法是更上一個抽象的緯度,對資料的定義是一些 Struct(貧血模型),而操作資料的方法都統一到具有相同函數簽名 (T, Action) => T 的 Reducer 中。
    FP:Struct(貧血模型) + Reducer = OOP:Bean(充血模型)
    同時 Redux 加上了 FP 中常用的 Middleware(AOP) 模式和 Subscribe 機制,給架構帶了極高的靈活性和擴充性。
    貧血模型、充血模型 參考:
    [https://en.wikipedia.org/wiki/Plain_old_Java_object](https://en.wikipedia.org/wiki/Plain_old_Java_object)
               

Redux 的缺點

  • Redux 核心僅僅關心資料管理,不關心具體什麼場景來使用它,這是它的優點同時也是它的缺點。
  • 在我們實際使用 Redux 中面臨兩個具體問題
    • Redux 的集中和 Component 的分治之間的沖突。
    • Redux 的 Reducer 需要一層層手動組裝,帶來的繁瑣性和易錯性。

Fish Redux 的改良

Fish Redux 通過 Redux 做集中化的可觀察的資料管理。然不僅于此,對于傳統 Redux 在使用層面上的缺點,在面向端側 flutter 頁面緯度開發的場景中,我們通過更好更高的抽象,做了改良。

一個元件需要定義一個資料(Struct)和一個 Reducer。同時元件之間存在着父依賴子的關系。通過這層依賴關系,

我們解決了【集中】和【分治】之間的沖突,同時對 Reducer 的手動層層 Combine 變成由架構自動完成,大大簡化了使用 Redux 的困難。

我們得到了理想的集中的效果和分治的代碼。

對社群标準的 follow

  • State、Action、Reducer、Store、Middleware 以上概念和社群的 ReduxJS 是完全一緻的。我們将原汁原味地保留所有的 Redux 的優勢。
  • 如果想對 Redux 有更近一步的了解,請參考   https://github.com/reduxjs/redux

Component

元件是對局部的展示和功能的封裝。 基于 Redux 的原則,我們對功能細分為修改資料的功能(Reducer)和非修改資料的功能(副作用 Effect)。

于是我們得到了,View、 Effect、Reducer 三部分,稱之為元件的三要素,分别負責了元件的展示、非修改資料的行為、修改資料的行為。

這是一種面向當下,也面向未來的拆分。在面向當下的 Redux 看來,是資料管理和其他。在面向未來的 UI-Automation 看來是 UI 表達和其他。

UI 的表達對程式員而言即将進入黑盒時代,研發工程師們會把更多的精力放在非修改資料的行為、修改資料的行為上。

元件是對視圖的分治,也是對資料的分治。通過逐層分治,我們将複雜的頁面和資料切分為互相獨立的小子產品。這将利于團隊内的協作開發。

關于 View

View 僅僅是一個函數簽名: (T,Dispatch,ViewService) => Widget

它主要包含三方面的資訊

  • 視圖是完全由資料驅動。
  • 視圖産生的事件/回調,通過 Dispatch 發出“意圖”,不做具體的實作。
  • 需要用到的元件依賴等,通過 ViewService 标準化調用。
    比如一個典型的符合 View 簽名的函數!
               
即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

關于 Effect

Effect 是對非修改資料行為的标準定義,它是一個函數簽名: (Context, Action) => Object

它主要包含四方面的資訊

  • 接收來自 View 的“意圖”,也包括對應的生命周期的回調,然後做出具體的執行。
  • 它的處理可能是一個異步函數,資料可能在過程中被修改,是以我們不崇尚持有資料,而通過上下文來擷取最新資料。
  • 它不修改資料, 如果修要,應該發一個 Action 到 Reducer 裡去處理。
  • 它的傳回值僅限于 bool or Future, 對應支援同步函數和協程的處理流程。
    比如:良好的協程的支援!
               
即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

關于 Reducer

Reducer 是一個完全符合 Redux 規範的函數簽名:(T,Action) => T

一些符合簽名的 Reducer!

即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

同時我們以顯式配置的方式來完成大元件所依賴的小元件、擴充卡的注冊,這份依賴配置稱之為 Dependencies。

是以有這樣的公式 Component = View + Effect(可選) + Reducer(可選) + Dependencies(可選)。

一個典型的組裝!

即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

通過 Component 的抽象,我們得到了完整的分治,多緯度的複用,更好的解耦。

Adapter

Adapter 也是對局部的展示和功能的封裝。它為 ListView 高性能場景而生,它是 Component 實作上的一種變化。

  • 它的目标是解決 Component 模型在 flutter-ListView 的場景下的 3 個問題
    • 1)将一個"Big-Cell"放在 Component 裡,無法享受 ListView 代碼的性能優化。
    • 2)Component 無法區分 appear|disappear 和 init|dispose 。
    • 3)Effect 的生命周期和 View 的耦合,在 ListView 的場景下不符合直覺的預期。
      概括的講,我們想要一個邏輯上的 ScrollView,性能上的 ListView ,這樣的一種局部展示和功能封裝的抽象。
      做出這樣獨立一層的抽象是,
      我們看實際的效果, 我們對頁面不使用架構,使用架構 Component,使用架構 Component+Adapter 的性能基線對比           
  • Reducer is long-lived, Effect is medium-lived, View is short-lived.

    我們通過不斷的測試做對比,以某 android 機為例:

  • 使用架構前 我們的詳情頁面的 FPS,基線在 52FPS。
  • 使用架構, 僅使用 Component 抽象下,FPS 下降到 40, 遭遇“Big-Cell”的陷阱。
  • 使用架構,同時使用 Adapter 抽象後,FPS 提升到 53,回到基線以上,有小幅度的提升。

Directory

推薦的目錄結構會是這樣

sample_page
-- action.dart
-- page.dart
-- view.dart
-- effect.dart
-- reducer.dart
-- state.dart
components
sample_component
-- action.dart
-- component.dart
-- view.dart
-- effect.dart
-- reducer.dart
-- state.dart           

上層負責組裝,下層負責實作,   同時會有一個插件提供, 便于我們快速填寫。

以閑魚的詳情場景為例的組裝:

即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點
即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

元件群組件之間,元件和容器之間都完全的獨立。

Communication Mechanism

  • 元件|擴充卡内通信
  • 元件|擴充卡間内通信
    即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點
簡單的描述:采用的是帶有一段優先處理的廣播, self-first-broadcast。
發出的 Action,自己優先處理,否則廣播給其他元件和 Redux 處理。
最終我們通過一個簡單而直覺的 dispatch 完成了元件内,元件間(父到子,子到父,兄弟間等)的所有的通信訴求。
           

Refresh Mechanism

資料重新整理

  • 局部資料修改,自動層層觸發上層資料的淺拷貝,對上層業務代碼是透明的。
  • 層層的資料的拷貝
    • 一方面是對 Redux 資料修改的嚴格的 follow。
    • 另一方面也是對資料驅動展示的嚴格的 follow。
      即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

視圖重新整理

  • 扁平化通知到所有元件,元件通過 shouldUpdate 确定自己是否需要重新整理
    即将開源 | 2億使用者背後的Flutter應用架構Fish Redux背景分層架構圖ReduxComponentAdapterDirectoryCommunication MechanismRefresh Mechanism優點

優點

資料的集中管理

  • 通過 Redux 做集中化的可觀察的資料管理。我們将原汁原味地保留所有的 Redux 的優勢,同時在 Reducer 的合并上,變成由架構代理自動完成,大大簡化了使用 Redux 的繁瑣度。

元件的分治管理

  • 元件既是對視圖的分治,也是對資料的分治。通過逐層分治,我們将複雜的頁面和資料切分為互相獨立的小子產品。這将利于團隊内的協作開發。

View、Reducer、Effect 隔離

  • 将元件拆分成三個無狀态的互不依賴的函數。因為是無狀态的函數,它更易于編寫、調試、測試、維護。同時它帶來了更多的組合、複用和創新的可能。

聲明式配置組裝

  • 元件、擴充卡通過自由的聲明式配置組裝來完成。包括它的 View、Reducer、Effect 以及它所依賴的子項。

良好的擴充性

  • 核心架構保持自己的核心的三層關注點,不做核心關注點以外的事情,同時對上層保持了靈活的擴充性。
    • 架構甚至沒有任何的一行的列印的代碼,但我們可通過标準的 Middleware 來觀察到資料的流動,元件的變化。
    • 在架構的核心三層外,也可以通過 dart 的語言特性 為 Component 或者 Adapter 添加 mixin,來靈活的組合式地增強他們的上層使用上的定制和能力。
    • 架構和其他中間件的打通,諸如自動曝光、高可用等,各中間件和架構之間都是透明的,由上層自由組裝。

精小、簡單、完備

  • 它非常小,僅僅包含 1000 多行代碼。
  • 它使用簡單,完成幾個小的函數,完成組裝,即可運作。
  • 它是完備的。

Fish Redux 目前已在阿裡巴巴閑魚技術團隊内多場景,深入應用。

最後 Talk is cheap, Show me the code,我們即将開源,敬請關注公衆号“閑魚技術”。

繼續閱讀