剛剛,阿里宣佈開源Flutter應用框架Fish Redux!

3月5日,閒魚宣佈在GitHub上開源Fish Redux,Fish Redux是一個基於 Redux 數據管理的組裝式 flutter 應用框架, 特別適用於構建中大型的複雜應用,它最顯著的特徵是 函數式的編程模型、可預測的狀態管理、可插拔的組件體系、最佳的性能表現。下文中,咱們將詳細介紹Fish Redux的特色和使用過程,如下內容來自InfoQ獨家對閒魚Flutter團隊的採訪和Fish Redux的開源文檔。前端

開源背景

在閒魚接入Flutter之初,因爲咱們的落地的方案但願是從最複雜的幾個主鏈路進行嘗試來驗證flutter完備性的,而咱們的詳情總體來說業務比較複雜,主要體如今兩個方面:git

  • 頁面須要集中狀態管理,也就是說頁面的不一樣組件共享一個數據來源,數據來源變化須要通知頁面全部組件。
  • 頁面的UI展示形式比較多(如普通詳情、閒魚幣詳情、社區詳情、拍賣詳情等),工做量大,因此UI組件須要儘量複用,也就是說須要比較好的進行組件化切分。

在咱們嘗試使用市面上已有的框架(google提供的redux以及bloc)的時候發現,沒有任何一個框架能夠既解決集中狀態管理,又能解決UI的組件化的,由於自己這兩個問題有必定的矛盾性(集中vs分治)。所以咱們但願有一套框架能解決咱們的問題,fish redux應運而生。程序員

fish redux自己是通過比較屢次的迭代的,目前你們看到的版本通過了3次比較大的迭代,實際上也是通過了團隊比較多的討論和思考。github

第一個版本是基於社區內的flutter_redux進行的改造,核心是提供了UI代碼的組件化,固然問題也很是明顯,針對複雜的詳情和發佈業務,每每業務邏輯不少,沒法作到邏輯代碼的組件化。編程

第二個版本針對第一個版本的問題,作出了比較重大的修改,解決了UI代碼和邏輯代碼的分治問題,但同時,按照redux的標準,打破了redux的原則,對於精益求精的閒魚團隊來說,不能接受;redux

所以,在第三個版本進行重構時,咱們確立了總體的架構原則與分層要求,一方面按照reduxjs的代碼進行了flutter側的redux實現,將redux的原則完整保留下來。另外一方面針對組件化的問題,提供了redux之上的component的封裝,並創新的經過這一層的架構設計提供了業務代碼分治的能力。性能優化

至此,咱們完成了fish redux的基本設計,但在後續的應用中,發現了業務組裝之後的代碼性能問題,針對該問題,咱們再次提供了對應的adapter能力,保障了在長列表場景下的big cell問題。目前,fish redux已經在線上穩定運行超過3個月以上,將來,期待fish redux給社區帶來更多的輸入。架構

Fish Redux技術解析

分層架構圖app

架構圖:主體自底而上,分兩層,每一層用來解決不通層面的問題和矛盾,下面依次來展開。框架

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

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 簽名的函數。

關於 Effect

Effect 是對非修改數據行爲的標準定義,它是一個函數簽名: (Context, Action) => Object
它主要包含四方面的信息

  • 接收來自 View 的「意圖」,也包括對應的生命週期的回調,而後作出具體的執行。
  • 它的處理多是一個異步函數,數據可能在過程當中被修改,因此咱們不崇尚持有數據,而經過上下文來獲取最新數據。
  • 它不修改數據, 若是修要,應該發一個 Action 到 Reducer 裏去處理。
  • 它的返回值僅限於 bool or Future, 對應支持同步函數和協程的處理流程。

好比良好的協程的支持:

關於 Reducer

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

同時咱們以顯式配置的方式來完成大組件所依賴的小組件、適配器的註冊,這份依賴配置稱之爲 Dependencies。

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

一個典型的組裝:

經過 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

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

以閒魚的詳情場景爲例的組裝:

組件和組件之間,組件和容器之間都徹底的獨立。

Communication Mechanism

  • 組件|適配器內通訊
  • 組件|適配器間內通訊

簡單的描述:採用的是帶有一段優先處理的廣播, self-first-broadcast。

發出的 Action,本身優先處理,不然廣播給其餘組件和 Redux 處理。最終咱們經過一個簡單而直觀的 dispatch 完成了組件內,組件間(父到子,子到父,兄弟間等)的全部的通訊訴求。

Refresh Mechanism

數據刷新

  • 局部數據修改,自動層層觸發上層數據的淺拷貝,對上層業務代碼是透明的。
  • 層層的數據的拷貝:

    • 一方面是對 Redux 數據修改的嚴格的 follow。
    • 另外一方面也是對數據驅動展現的嚴格的 follow。

視圖刷新

扁平化通知到全部組件,組件經過 shouldUpdate 肯定本身是否須要刷新。

Fish Redux的優勢

數據的集中管理

經過 Redux 作集中化的可觀察的數據管理。咱們將原汁原味地保留全部的 Redux 的優點,同時在 Reducer 的合併上,變成由框架代理自動完成,大大簡化了使用 Redux 的繁瑣度。

組件的分治管理

組件既是對視圖的分治,也是對數據的分治。經過逐層分治,咱們將複雜的頁面和數據切分爲相互獨立的小模塊。這將利於團隊內的協做開發。

View、Reducer、Effect 隔離

將組件拆分紅三個無狀態的互不依賴的函數。由於是無狀態的函數,它更易於編寫、調試、測試、維護。同時它帶來了更多的組合、複用和創新的可能。

聲明式配置組裝

組件、適配器經過自由的聲明式配置組裝來完成。包括它的 View、Reducer、Effect 以及它所依賴的子項。

良好的擴展性

核心框架保持本身的核心的三層關注點,不作核心關注點之外的事情,同時對上層保持了靈活的擴展性。

  • 框架甚至沒有任何的一行的打印的代碼,但咱們可經過標準的 Middleware 來觀察到數據的流動,組件的變化。
  • 在框架的核心三層外,也能夠經過 dart 的語言特性 爲 Component 或者 Adapter 添加 mixin,來靈活的組合式地加強他們的上層使用上的定製和能力。
  • 框架和其餘中間件的打通,諸如自動曝光、高可用等,各中間件和框架之間都是透明的,由上層自由組裝。

精小、簡單、完備

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

關於將來

開源以後,閒魚打算經過如下方式來維護Fish Redux:

  • 經過後續的一系列的對外宣傳,吸引更多的開發者加入或者使用。目前Flutter生態裏,應用框架仍是空白,有機會成爲事實標準;
  • 配合後續的一系列的閒魚Flutter移動中間件矩陣作開源;
  • 進一步提供,一系列的配套的開發輔助調試工具,提高上層Flutter開發效率和體驗。

Fish Redux 目前已在阿里巴巴閒魚技術團隊內多場景,深刻應用。最後 Talk is cheap, Show me the code,咱們今天正式在GitHub上開源,更多內容,請到GitHub瞭解。

GitHub地址:https://github.com/alibaba/fish-redux



本文做者:閒魚技術-吉豐

閱讀原文

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

相關文章
相關標籤/搜索