做者:閒魚技術-海瀦前端
fish_redux是閒魚技術團隊打造的開源flutter應用開發框架,旨在解決頁面內組件間的高內聚、低耦合問題。開源地址:github.com/alibaba/fis…react
redux對於前端的同窗來講是一個比較熟悉的框架了,fish_redux借鑑了redux單項數據流思想。在flutter上說到redux,你們可能第一反應會類比到react上的react_redux。在react_redux中有個重要的概念——connect,git
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])github
簡單得說,connect容許使用者從Redux store中獲取數據並綁定到組件的props上,能夠dispatch一個action去修改數據。redux
那麼fish_redux中的connector是作什麼的呢?爲何說connector解決了組件內聚的問題?咱們應該如何理解它的設計呢?框架
儘管都起到了鏈接的做用,但fish_redux與react_redux在抽象層面有很大的不一樣。ide
fish_redux自己是一個flutter上的應用框架,創建了本身的component體系,用來解決組件內的高內聚和組件間的低耦合。從connector角度來講,如何解決內聚問題,是設計中的重要考量。函數
fish_redux本身製造了 Component
樹, Component
聚合了state和dispatch,每個子 Component
的state經過 connector
從父 Component
的state中篩選。如圖所示:源碼分析
能夠看到,fish_redux的connector的主要做用把父子 Component
關聯起來,最重要的操做是filter。state從上之下是一個嚴謹的樹形結構,它的結構複用了 Component
的樹形結構。相似一個漏斗形的數據管道,管理數據的分拆與組裝。它表達瞭如何組裝一個 Component
。優化
而對於react_redux來講,它主要的做用在於把react框架和redux綁定起來,重點在於如何讓React component具備Redux的功能。
從圖中能夠看到,react_redux和React是平行的結構,通過 mapStateToProps
後的state也不存在嚴謹的樹形結構,即對於一個React component來講,它的state來自於Redux store而不是父component的state。從框架設計的角度來講,react_redux最重要的一個操做就是attach。
說完概念,咱們從源碼的角度來看看fish_redux中的connector是如何運做的,以fish_redux提供的example爲例。
class ToDoListPage extends Page<PageState, Map<String, dynamic>> {ToDoListPage(): super(...dependencies: Dependencies<PageState>(adapter: ToDoListAdapter(),slots: <String, Dependent<PageState>>{'report': ReportConnector() + ReportComponent()}),...);}
複製代碼
在ToDoListPage的構造函數中,向父類構造傳遞了一個 Dependencies
對象,在構造 Dependencies
時,參數 slots
中包含了名叫"report"的item,注意這個item的生成,是由一個 ReportConnector
+ ReportComponent
產生的。
從這裏咱們得出一個簡單卻很是重要的結論:
在fish_redux中,一個Dependent = connector + Component 。
Dependent
表明頁面拼裝中的一個單元,它能夠是一個 Component
(經過buildComponent函數產生),也能夠是一個 Adapter
(由buildAdapter函數產生)。這樣設計的好處是,對於View拼裝操做來講, Dependent
對外統一了API而不須要透出更多的細節。
根據上面咱們得出的結論, connector
用來把一個更小的 Component
單元連接到一個更大的 Component
或 Adapter
上。這與咱們以前的描述相符合。
知道了 connector
的基本做用,咱們來看一下它到底連接了哪些東西以及如何連接。
先來看一下ReportConnector類的定義:
class ReportConnector extends ConnOp<PageState, ReportState>
複製代碼
ReportConnector
繼承了 ConnOp
類,全部 connector
的操做包括**+**操做,都來自於 ConnOp
類。
既然是數據管道,就會有獲取和放置。
set
函數的入參很好得表達了 T
和 P
的意思,即把一個 P
類型的 subState
合併到 T
類型的 state
中。
再回頭看 get
函數,就很好理解了, get
函數表達的就是如何從 T
類型的 state
中獲取 P
類型的 subState
供 Dependent
使用。
+
操做符的重載是咱們最初看到connector做用的地方,也是connector發揮做用的入口。
Logic
是 Component
和 Adapter
的父類,它表示頁面組裝元素的邏輯層,裏面包含了 reducer
/ effect
/ higherEffect
等與邏輯相關的元素以及它的組裝過程。
operator +
調用了 createDependent
函數,接着會調用到 _Dependent
類的構造函數,這裏將 logic
和 connector
放入 _Dependent
內部,在後面fish_redux對 Component
組裝的過程當中,connector會隨着外部對 _Dependent
中函數的調用發揮做用。
connector正式登場
鋪墊了這麼多,是該connector正式發揮做用的時候了。
咱們以 Component
爲例,會調用到 _Dependent
的 buildComponent
函數:
Widget buildComponent(MixedStore<Object> store, Get<T> getter) {final AbstractComponent<P> component = logic;return component.buildComponent(store, () => connector.get(getter()));}
複製代碼
這裏的 logic
實際就是一個 Component
對象,在調用 Component
的 buildComponent
函數的時候,使用 get
函數從一個大的父state中獲取到當前 Component
須要的數據集。接下去,這個變換後的子state將被用在例如 ViewBuilder
或 Redcuer
函數中。
這是connector在數據獲取上的做用。
仍是在 _Dependent
類裏面,看 createSubReducer
函數:
SubReducer<T> createSubReducer() {final Reducer<P> reducer = logic.reducer;return reducer != null ? connector.subReducer(reducer) : null;}
複製代碼
首現從一個 Logic
(這裏其實是一個 Component
)對象中獲取到外部設置進來的 reducer
,接着調用 subReducer
返回一個 SubReducer
對象。 SubReducer
是一個被wrap後的 Reducer。
subReducer
的實如今 MutableConn
中, ConnOp
繼承了 MutableConn
類,也得到了這個能力。
SubReducer<T> subReducer(Reducer<P> reducer) {return (T state, Action action, bool isStateCopied) {final P props = get(state);if (props == null) {return state;}final P newProps = reducer(props, action);final bool hasChanged = newProps != props;final T copy = (hasChanged && !isStateCopied)? _clone<T>(state) : state;if (hasChanged) {set(copy, newProps);}return copy;};}
複製代碼
它首現經過 get
函數獲得一個變換後的數據集 props
,接着調用原始的 reducer
函數進行邏輯處理,這裏有一個優化也是 SubReducer
的做用,若是數據集在通過 reducer
處理以後發生了變化,而且state已經被copy過一次了(isStateCopied==true),就直接把 newProps
經過 set
函數更新到state中去。(這個優化能夠防止多個子state發生變化的時候父state被拷貝屢次)。
至此,connector在數據更新上的做用也體現出來了。
最後,就好理解ReportConnector的實現了:
class ReportConnector extends ConnOp<PageState, ReportState> {@overrideReportState get(PageState state) {final ReportState reportState = ReportState();reportState.total = state.toDos.length;reportState.done =state.toDos.where((ToDoState tds) => tds.isDone).toList().length;return reportState;}@overridevoid set(PageState state, ReportState subState) {}}
複製代碼
很明顯的,在 get
函數中, ReportState
從 PageState
中得到了total/done字段。
總結
閒魚客戶端的詳情頁徹底使用了fish_redux進行了重構,經過高內聚的 Component+connector
形式,使得 Component
能夠被大量複用,很好得支持了5種類型的詳情頁。將來咱們會基於fish_redux強大的擴展能力製做更多組件來知足不一樣業務對於框架的需求。