開發

第五人格直播鬼灯葬月:Flutter高內聚組件怎么做?閑魚打造開源高效方案!

fish_redux是閑魚技術團隊打造的開源flutter應用開發框架,旨在解決頁面內組件間的高內聚、低耦合問題??吹刂罰篽ttps://github.com/alibaba/fish-redux

從react_redux說起

redux對于前端的同學來說是一個比較熟悉的框架了,fish_redux借鑒了redux單項數據流思想。在flutter上說到redux,大家可能第一反應會類比到react上的react_redux。在react_redux中有個重要的概念——connect,

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

簡單得說,connect允許使用者從Redux store中獲取數據并綁定到組件的props上,可以dispatch一個action去修改數據。

那么fish_redux中的connector是做什么的呢?為什么說connector解決了組件內聚的問題?我們應該如何理解它的設計呢?

connector in fish_redux

盡管都起到了連接的作用,但fish_redux與react_redux在抽象層面有很大的不同。

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為例。

123456789101112class ToDoListPage extends Page<PageState, Map> {ToDoListPage()super(...dependencies: Dependencies(adapter: ToDoListAdapter(),slots: <String, Dependent>{'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單元鏈接到一個更大的 ComponentAdapter上。這與我們之前的描述相符合。

connector到底是什么?

知道了 connector的基本作用,我們來看一下它到底鏈接了哪些東西以及如何鏈接。

先來看一下ReportConnector類的定義:

class ReportConnector extends ConnOp

ReportConnector繼承了 ConnOp類,所有 connector的操作包括+操作,都來自于 ConnOp類。

set/get

既然是數據管道,就會有獲取和放置。

set函數的入參很好得表達了 TP的意思,即把一個 P類型的 subState合并到 T類型的 state中。

再回頭看 get函數,就很好理解了, get函數表達的就是如何從 T類型的 state中獲取 P類型的 subStateDependent使用。

operator +

+操作符的重載是我們最初看到connector作用的地方,也是connector發揮作用的入口。

LogicComponentAdapter的父類,它表示頁面組裝元素的邏輯層,里面包含了 reducer/ effect/ higherEffect等與邏輯相關的元素以及它的組裝過程。

operator +調用了 createDependent函數,接著會調用到 _Dependent類的構造函數,這里將 logicconnector放入 _Dependent內部,在后面fish_redux對 Component組裝的過程中,connector會隨著外部對 _Dependent中函數的調用發揮作用。

connector正式登場

鋪墊了這么多,是該connector正式發揮作用的時候了。

get

我們以 Component為例,會調用到 _DependentbuildComponent函數:

1234Widget buildComponent(MixedStore store, Get getter) {final AbstractComponent component = logic;return component.buildComponent(store, () => connector.get(getter()));}

這里的 logic實際就是一個 Component對象,在調用 ComponentbuildComponent函數的時候,使用 get函數從一個大的父state中獲取到當前 Component需要的數據集。接下去,這個變換后的子state將被用在例如 ViewBuilderRedcuer函數中。

這是connector在數據獲取上的作用。

set

還是在 _Dependent類里面,看 createSubReducer函數:

1234SubReducer createSubReducer() {final Reducer reducer = logic.reducer;return reducer != null ? connector.subReducer(reducer) : null;}

首現從一個 Logic(這里實際上是一個 Component)對象中獲取到外部設置進來的 reducer,接著調用 subReducer返回一個 SubReducer對象。 SubReducer是一個被wrap后的 Reducer。

subReducer的實現在 MutableConn中, ConnOp繼承了 MutableConn類,也獲得了這個能力。

12345678910111213141516SubReducer subReducer(Reducer 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(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

最后,就好理解ReportConnector的實現了:

class ReportConnector extends ConnOp<PageState, ReportState> {  @override  ReportState 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;  }
 @override  void set(PageState state, ReportState subState) {}}

很明顯的,在 get函數中, ReportStatePageState中獲得了total/done字段。

總結

閑魚客戶端的詳情頁完全使用了fish_redux進行了重構,通過高內聚的 Component+connector形式,使得 Component可以被大量復用,很好得支持了5種類型的詳情頁。未來我們會基于fish_redux強大的擴展能力制作更多組件來滿足不同業務對于框架的需求。 

我還沒有學會寫個人說明!

四面受敵!ARM暫停與華為合作,芯片備胎計劃還能執行么?

上一篇

為什么你應當選擇 PostgreSQL 而不是 Oracle?

下一篇

你也可能喜歡

Flutter高內聚組件怎么做?閑魚打造開源高效方案!

長按儲存圖像,分享給朋友

ITPUB 每周精要將以郵件的形式發放至您的郵箱


微信掃一掃

微信掃一掃