Flutter高內聚組件怎麼作?阿里閒魚打造開源高效方案!

fish_redux是閒魚技術團隊打造的flutter應用開發框架,旨在解決頁面內組件間的高內聚、低耦合問題。開源地址:https://github.com/alibaba/fish-redux前端

從react_redux提及

redux對於前端的同窗來講是一個比較熟悉的框架了,fish_redux借鑑了redux單項數據流思
想。在flutter上說到redux,你們可能第一反應會類比到react上的react_redux。在react_redux中有個重要的概念——connectreact

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

簡單得說,connect容許使用者從Redux store中獲取數據並綁定到組件的props上,能夠dispatch一個action去修改數據。github

那麼fish_redux中的connector是作什麼的呢?爲何說connector解決了組件內聚的問題?咱們應該如何理解它的設計呢?redux

connector in fish_redux

儘管都起到了鏈接的做用,但fish_reduxreact_redux在抽象層面有很大的不一樣。框架

fish_redux自己是一個flutter上的應用框架,創建了本身的component體系,用來解決組件內的高內聚和組件間的低耦合。從connector角度來講,如何解決內聚問題,是設計中的重要考量。ide

fish_redux本身製造了Component樹,Component聚合了statedispatch,每個子Componentstate經過connector從父Componentstate中篩選。如圖所示:函數

能夠看到,fish_reduxconnector的主要做用把父子Component關聯起來,最重要的操做是filterstate從上之下是一個嚴謹的樹形結構,它的結構複用了Component的樹形結構。相似一個漏斗形的數據管道,管理數據的分拆與組裝。它表達瞭如何組裝一個Component源碼分析

而對於react_redux來講,它主要的做用在於把react框架和redux綁定起來,重點在於如何讓React component具備Redux的功能。優化

從圖中能夠看到,react_reduxReact是平行的結構,通過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單元連接到一個更大的
ComponentAdapter上。這與咱們以前的描述相符合。

connector究竟是什麼

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

先來看一下ReportConnector
類的定義:

class ReportConnector extends ConnOp<PageState, ReportState>

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_reduxComponent組裝的過程當中,connector會隨着外部對_Dependent中函數的調用發揮做用。

connector正式登場

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

get

咱們以Component爲例,會調用到_DependentbuildComponent函數:

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

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

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

set

仍是在_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

最後,就好理解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強大的擴展能力製做更多組件來知足不一樣業務對於框架的需求。


原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索