Fish Redux 全域性Store-AppRoute使用指南

升級之路發表於2019-04-08

在學習怎麼使用AppRoute前,先了解下這幾個問題,確定這是否是你需要的

  1. 為什麼Redux.js和Vuex是全域性Store?

    前端的單頁面應用越來越複雜,一個model變化會引起另外一個model變化,又可能引起檢視變化。Redux的三大原則,使state的變化可預測成為可能。其中第一條就是單一資料來源,這體現了集中思想。應用維護一顆狀態樹,而資料變化驅動檢視重新重新整理。單一的狀態樹更方便資料的管理,由於生命週期和App一致,元件間資料也更方便傳遞分享(如登入資訊)。

  2. 為什麼Fish Redux不是這樣的?

    Fish Redux一開始只支援Page級別Stroe是業務的客觀訴求。Flutter作為一個新技術棧要用在一個上億使用者的閒魚App中,需要風險控制和隔離,只能先改寫一些頁面,驗證改進迭代再逐步替換。

  3. 我是否需要使用AppRoute?

    如果你的訴求和閒魚一樣,那選擇的是高難度路線,需要在集中和分治間做長久鬥爭。建議使用HybridRoute能省點時間,也方便後續改造。不然,那就用AppRoute吧,充分享受Flutter和Redux帶來的優越性。

設計思想

作者新增了Route這一層,它的核心作用是產生一個Page,對應不同場景,實現了三個具體的XXXRoute

/// Route 抽象類,只提供返回一個page的能力
abstract class AbstractRoutes {
  Widget buildPage(String path, dynamic arguments);
}

/// AppRoutes實現多page公用一個store,適合全新App使用
class AppRoutes<T> implements AbstractRoutes {
  final Map<String, Dependent<T>> pages;
  final PageStore<T> _store;
    ...
}

/// 一個page,一個store,適合部分頁面改寫flutter的App使用,如現在的閒魚 
class PageRoutes implements AbstractRoutes {
  final Map<String, Page<Object, dynamic>> pages;
    ...
}

/// 混合Routes,可以同時使用AppRoutes和PageRoutes,適合都要的App,如未來一段時間的閒魚
class HybridRoutes implements AbstractRoutes {
  final List<AbstractRoutes> routes;
    ...
}    
複製程式碼

使用方式

  1. 定義一個AppRoute

    /// app_route.dart
    /// 因為是全域性的,這裡用單例,也比較方便呼叫
    class AppRoute {
      static AbstractRoutes _global;
      static AbstractRoutes get global {
        if (_global == null) {
          _global = AppRoutes(preloadedState: AppState.initialState(), pages: {
            // 這裡有兩種寫法,效果是一樣的,帶操作符的寫法比較生動,也簡短些。
            // RoutePath.todoList: TodoListPage().asDependent(TodoListConn()),
            RoutePath.todoList: TodoListConn() + TodoListPage(),
            RoutePath.todoDetail: TodoDetailConn() + TodoDetailPage(),
          });
        }
        return _global;
      }
    }
    
    /// 減少魔法欄位,這些常量放在constant裡也可以,放在這裡只是方便。
    class RoutePath {
      static const String todoList = 'todo_list';
      static const String todoDetail = 'todo_detail';
    }
    複製程式碼
  2. 定義一個AppState,用來作為狀態樹的根節點

    /// screens/todo_list/state.dart
    class AppState implements Cloneable<AppState> {
      TodoListState todoListState;
      TodoDetailState todoDetailState;
    
      AppState(this.todoListState, this.todoDetailState);
    
      @override
      AppState clone() {
        return AppState(todoListState, todoDetailState);
      }
    
      AppState.initialState()
          : todoListState = initState(null),
            todoDetailState = TodoDetailState();
    }
    複製程式碼
  3. 定義Connect,描述subState和父State之間的轉化關係

    /// screens/todo_list/state.dart
    /// get定義從大state取小state,set定義小state怎麼改變大state
    class TodoListConn extends ConnOp<AppState, TodoListState> {
      @override
      TodoListState get(AppState state) => state.todoListState;
      
      @override
      void set(AppState state, TodoListState subState) =>
          state.todoListState = subState;
    }
    複製程式碼
  4. 替換原來Page產生的方式

    /// main.dart
    home: AppRoute.global.buildPage(RoutePath.todoList, null),
    複製程式碼
  5. 額外處理(有資料跳轉的Page)

    /// screens/todo_detail/reducer.dart
    /// 頁面跳轉可能會有資料傳遞,比如list到detail頁面,需要傳遞id,
    /// 這是很常見的,那資料什麼時候傳遞?自然是page生成的時候,可以看到[AbstractRoutes]
    /// 的buildPage接受一個arguments引數,dynamic你可以傳任意結構資料,推薦傳Map,原生json樣式
    /// 如果arguments不為null,Route會幫我們發一個route的Action,
    /// 所以我們需要在對應的Reducer裡處理一下
    Reducer<TodoDetailState> buildReducer() {
      return asReducer<TodoDetailState>(<Object, Reducer<TodoDetailState>>{
        RouteAction.route: _route,
          ...
      });
    }
    
    /// 接到route Action,更新下state的id
    TodoDetailState _route(TodoDetailState state, Action action) {
      final TodoDetailState newState = state.clone();
      newState.id = action.payload['id'];
      return newState;
    }
    複製程式碼

對比體驗Flutter Redux

一直以來Fish Redux和Flutter Redux的最佳實踐並不同,一個是Page級別的狀態管理,一個是App級別狀態管理,所以算是錯位競品。在Fish Redux加入Route後,它也算完全體了,這兩個都用過後我簡單對比下:

  1. 方便度

    Flutter Redux呼叫簡單,想要深入理解API含義,會難一些。Fish Redux概念多,感覺是把二維陣列flat了,大堆的定義和概念,可能會勸退小部份沒耐心的同學。然而框架意圖是清晰的,而且程式碼也有精心設計的痕跡。這點對我而言平分秋色。

  2. 文件例子

    Flutter Redux優勝,文件和例子都比剛開源的Fish Redux多很多,Fish Redux文件不多但都有中文版哦

  3. 問題響應

    Fish Redux優勝,Github上Issue基本能在12小時內得到作者或熱心網友的回覆和解答。而Flutter Redux的作者維護了很多個庫,精力有限,所以Issue處理速度可以說是拖拖拉拉了

  4. 完備度

    功能上都是完備的,形式上Flutter Redux需要依賴兩個庫,Fish Redux沒有任何其他三方依賴會好一點點

  5. 效能

    其實狀態管理的初衷並不會很在意效能,畢竟它只是個狀態管理啊,管理都需要額外成本的。但是fish redux確實關注了並對於大列表場景做了比較突出的優化,效能提升剛剛的。

總結:Fish Redux完全可以成為Flutter狀態管理的備選方案了,而且它還正以肉眼可見的速度在成長。

後續

對比過後覺得Fish Redux不錯,想推銷給長官

我:報告老闆,ali的Fish Redux老NB了,我們專案要不用這個吧?

老闆:哪NB了?

我:巴拉巴拉~

老闆:哪些大廠在用了

我:額,貌似就ali

老闆:單元測試覆蓋多少?

我:快50%吧

老闆:效能提升多少,有benchmark嗎?

我:。。。

抱歉,以上是我腦補的,但以我老闆的專業度,這些問題必問。。。

本文原始碼地址:https://github.com/hyjfine/flutter_redux_sample/tree/fish-redux-route

(完)

@子路宇, 本文版權屬於再惠研發團隊,歡迎轉載,轉載請保留出處。

相關文章