天天看點

帶你重新“玩轉”Flutter

作者:閑魚技術——匠修

Flutter也許不再是非常熱門時髦的話題了,但作為一項已經逐漸進入規模化實踐的技術,它的價值已經初步獲得認可,後續應該有不錯的生命力。作為較早期的Flutter實踐者,我一直在思考Flutter的技術價值以及如何釋放這些價值,本篇嘗試從一個新的視角去結構化的梳理Flutter的技術價值并做對應的應用分析。這裡不會涉及到Flutter具體領域的技術點,但是會結合我們團隊過去的探索實踐,在技術使用政策的層面做一些總結,希望能幫助到小夥伴們在開發實踐中思考提煉,擡頭看路,仰望星空,找到未來的創新方向。

一. 前端有些啥問題

要溯源Flutter, 就得從前端說起了。這裡的前端是相對于後端的概念,大概泛指通過圖形界面實作使用者互動的終端技術。這個領域一直很有活力,伴随着網際網路一路走來,很多新思路和新技術都有點眼花缭亂:上古的MVC已經不大提起了,老一輩的MVP,MVVM也逐漸暗淡,新一輩的Vue,Angular,React方興未艾,還有Redux, Mobx, Hooks推波助瀾。這些技術都像是一個個有感情的小生命,有的熱情,有的文藝,有的高冷,十分熱鬧。也許你會想:它們都在說個啥?它們都想解決一個啥問題?

1. 節點,連接配接和網絡

網際網路是這一切存在的大背景,網際網路有3個大要素:節點,連接配接和網絡拓撲結構。每一個要素的進化都會給前端帶來更新和變革:從PC網際網路到移動網際網路再到物聯網是節點在進化;充滿想象的5G時代是連接配接在進化;從中心化到分布式是網絡拓撲結構在進化。前端的使命是讓使用者(人)高效的嵌入到網絡之中,讓人和裝置融合為一個有生命的網絡“節點”。

帶你重新“玩轉”Flutter

PS:網絡模型可以适配到前端的很多場景,裡面的“節點”可以代指手機裝置,應用程式,程序,頁面,甚至元件。

2. 節點與前端技術

前端技術是使用者(人)與裝置的粘合劑,讓二者有機的構成一個網絡節點。我嘗試剖析下前端技術在節點中的形态:首先它需要提供界面給使用者,并響應使用者的互動;需要管理和遠端節點的通信,交換資訊;還需要管理目前節點的資訊狀态;最後,為了友善的嵌入到網絡中,節點需要一個友好的“外觀”,就是生命周期。生命周期描述了節點的誕生,消亡,所擁有的權益,和所承擔的責任。

帶你重新“玩轉”Flutter

PS:這個分治模型也具有一定的通用性,可以适用在應用程式,頁面,或者元件。

3. 前端要解決的問題

當然,前端技術作為一種軟體技術,在日常的工程開發中也需要基礎的建構部署支撐。那麼,綜合前面的讨論,前端要解決的基礎要素問題有:

  • 遠端通信
  • 界面管理
  • 生命周期
  • 狀态管理
  • 建構部署

這裡面生命周期管理是一個比較“隐形”的問題,它其實就是建構“外觀”描述,也就是“元件化”。這些基礎要素問題的結構關系大概是:

帶你重新“玩轉”Flutter

這幾個基礎要素問題的解決往往不能孤立的進行,在設計解決方案的時候需要彼此配合。前端領域的技術方案通常都會合并解決其中的幾個問題,比如:React解決界面管理和生命周期;Vue解決了界面管理,狀态管理,和生命周期;Redux專注解決狀态管理;GraphQL,BFF,Serverless解決遠端通信;webpack解決建構部等等...

那麼,Flutter解決了什麼問題?或者,Flutter可以解決哪些問題呢?

二. Flutter都有些啥

有趣的技術方案應該有自己旗幟鮮明的個性,做架構設計往往是遇到了獨特的問題或者發現了更好的工具。是以,使用Flutter做解決方案的時候,應該梳理下Flutter都帶來了些什麼。一般來說,主要有Dart語言,Dart運作時(VM),GUI架構,和相關的開發建構工具。

1. Dart語言

Dart語言不好說有多優秀,總的來說是一門工程“友好”的現代語言。也許感覺平淡無奇,但官方在介紹Flutter的時候,還略有“驕傲”的展示了它的包容性:它能較為原生的支援聲明式,過程式,面向對象,函數式,和響應式等業務場景,它給技術方案實作提供了很自由的範式空間,這是值得發掘和嘗試的。結合我自己的實踐,有幾個點值得一說:

  • 支援類似“協程”處理(async/await/yield):Dart是單線程的,但是支援異步。這一點需要在使用中去體會,不展開說,了解深了以後,可能對很多問題都有新解法。
  • 提供Stream:當你要進階學習Flutter的時候,很多地方能見到它。它能做流式資料處理,函數組合,傳輸控制,屬于進階必知必會。
  • mixin特性:這是很有用的特性,尤其在架構設計方面。它提供了新的組合方式,在做功能組合的時候,Dart可以使用對象注入,或者高階函數,或者mixin。

2. Dart-Runtime / VM

當你引入Flutter,你就有一個Dart-Runtime啦。當然,它本來的意義是支援Flutter運作的,然而就像JS的V8引擎一樣,本來是用來支援H5頁面的,後面的腦洞就越開越大了。當然Dart沒有JS的動态能力(特殊的場合也可以),但它是跨端的,而且aot性能有保障,發掘空間較大,還能同時覆寫幾乎所有場景,手機app開發,PC的建構部署,服務端,甚至serverless運作時。

3. Flutter應用開發架構

Flutter帶來了高性能的跨平台GUI Framework,這沒啥可說的,用就是了!但如果已經有了自己的開發生态,舍棄成本太大,又想“借用”下Flutter的技術優勢,大概有幾種方案:

  • 語言轉換,基本上就是把其他語言的布局結果交給Flutter Framework。
  • 中層Framework介入,選擇一棵樹介入,這樣對接面會小一些。
  • 底層Framework替換,整個替換掉Flutter Framework, 直接使用底層的Engine。
帶你重新“玩轉”Flutter

當然,還有一種方式就是混合開發了,需要實作混合棧管理,而且在1.22版本以後,Flutter更新了Navigator元件,提供了Navigator 2.0,這對混合開發是一個利好。

4. 開發建構工具

值得一提的是HotReload,誰用誰知道。熱部署不僅界面開發需要,這是個通用需求,如果在服務端通過Dart實作一個,也是極好的。

Flutter的建構工具并無太多亮點,而且因為Dart語言在Flutter中不支援反射(産物太大,增加包體積),業務架構實作上很多需要依賴于編譯時處理的技術,這對建構系統的能力有較大依賴的。換一個角度,因為這一領域尚沒有成熟方案,加上Dart的工程“友好”,可能實作由一種語言來統一前端開發,後端開發,以及建構和部署,真做到了會很酷!

三. Flutter可以有些啥玩法

讨論前端要解決的幾個基礎要素問題,也讨論了Flutter帶來的技術工具,結合這兩點,看看利用Flutter都能做些啥。

1. 遠端通信

“遠端通信”是一個做了抽象的概念,具體形式會根據“節點”的定義不同而不同。可以是指實體裝置之間的通信(手機與伺服器),也可以指子產品之間(頁面與另一個頁面)的通信,還可以指元件之間(兩個StatefulWidget之間)的通信。因為在Flutter中“一切皆是Widget”,是以簡化的看,Flutter裡面大概分為兩種情形:面向伺服器的通信群組件(Widget)之間的通信。

  • 面向伺服器的通信:這方面有傳統的Restful,GraphQL,還有興起于“雲原生”概念的Serverless。借助于Dart的能力延伸,Dart-Runtime可以成為Serverless的可選容器,那麼前後兩端都可以使用Dart來開發,如果再用Dart補足中間的建構部署pipeline,那麼可以搭建出一個很“雲原生”概念的應用程式開發流(dev flow):
帶你重新“玩轉”Flutter

Flutter的跨端能力結合雲原生的彈性部署,前後端的實作可以放到一起,它們之間的調用也變得簡單自然,有理由相信這會是一個高效的開發方式。閑魚已經做了前期的探索,并在業務中嘗試落地。

此外,GraphQL與Flutter也是值得嘗試的組合,GraphQL和React範式在理念上很對味口,但是我們并沒有嘗試過。

  • 元件之間的通信:Flutter提供的基礎業務程式設計元件是StatefulWidget和StatelessWidget,它們是按樹形結構來組織的,并提供了InheritedWidget來支援它們之間的通信,在通信方式可以總結出3種:
      1. Notify型:通知/監聽模式,Flutter提供了ValueNotifier和ChangeNotifier, 簡單友善,适用于輕量資訊通信,參見Provider。
      1. Transmission型:資料傳輸模式,Dart提供了Stream來支援這種模式。使用靈活,擴充友善,幾乎是架構設計必備,參見BLoC。
      1. Invoke型: 接口調用模式,類似輕量RPC方式,在Flutter的應用架構設計中竟然很少見到。它确實有些重,但是有前面的模式沒有的優勢:它是雙向的。
帶你重新“玩轉”Flutter

2. 狀态管理

狀态管理是個大問題,它由兩個元素構成:狀态和處理狀态的邏輯。在代碼實作上,狀态就是資料,邏輯就是函數。當它們變得複雜的時候,解決的辦法其實就是拆分,然後再合理的組合。我嘗試從狀态和邏輯的拆分選擇上來列舉所有可能的解法:

  • 1. 狀态不拆,拆分邏輯: 這種做法的特點全局共用一個狀态結構體對象,所有狀态全部放在這個對象中,叫做“統一狀态管理”。隻有一個資料對象,就消除了各個處理邏輯之間資訊共享問題,邏輯内部沒有狀态,變得非常的純粹(比如純函數來實作),再将邏輯以合适的方式組合成一個整體,實作上界限清晰,簡單優雅,代表的方案就是Redux。

    如果采用這種設計方案,推薦使用函數式風格來實作,這倒不是因為函數式“更高效”,“更優雅”之類的,而是它與函數式的思路十分的契合,在實作上更容易把握住思路。用面向對象來實作也并沒有問題,最後的效果取決于你所面臨的工程環境和使用者。

    這種狀态管理好處顯而易見:一處狀态發生改變,不需要到處發事件同步。但是如果用在複雜大業務上面(比如一個有數十個頁面流程的業務産品),狀态必定是複雜的,狀态結構體必然是龐大的。Redux的方式很好的管理了邏輯,如果要管理一個統一龐大的狀态資料,也許記憶體級别的SQL是個不錯的主意,GraphQL-Client給了我們啟發,我們也正在嘗試實踐。

帶你重新“玩轉”Flutter
  • 2. 邏輯不拆,拆分狀态: 我把這種叫做“步進狀态管理”,實際上這類似于狀态機模式。但如果要真正使用,狀态必須是有限的,而且不能經常變動。這與網際網路持續多變的業務需求實際是不符合的,是以采用這種方式的設計幾乎沒有。但在一些很嚴肅的業務場景中,比如交易流程,一旦定下來就不容易變動。這時,步進狀态管理就很合适了。
帶你重新“玩轉”Flutter
  • 3. 同時拆分邏輯和狀态: 這是容易想到方案,在更細的粒度上,将狀态和它對應的處理邏輯拆分打包,變成更小的域(scope),然後統一協調這些子域(subModel),我把這種叫做“組合狀态管理”。進一步的方案可以統一定義subModel的基礎行為,然後引入排程器來協調管理它們,subModel之間還可以共享上下文來共享資訊等等。

    這種思路形式自由,可以采用的實作方式很多,經典的面向對象當然不在話下,scoped_model采用的就是這種思路,scoped_model實作的簡單易懂,同時能力也比較有限。當然也可以采用函數式的方式,高階函數+閉包也能很好的實作,但是圈内沒有看到有相關的設計實作。還有一點,Dart提供了Mixin特性,通過這個特性,可以得到更簡潔的實作方案。比如,可以沉澱很多特定的Model,然後通過with選擇組合到業務Model中來(參考Flutter Framework中WidgetsBinding的實作)。目前flutter-hook在做這方面的探索,flutter-hook看起來有些迷惑,其核心就是利用了Dart的Mixin特性來組合狀态。

帶你重新“玩轉”Flutter

3. 界面管理

Flutter使用了響應式UI,目的就是讓業務開發減少界面的管理工作,隻要提供好頁面“描述”就行了。雖然Flutter内建了類似Virtual Dom的Diff機制,但是,做這個Diff也是要費性能的,如果我們在架構設計上能内建的把Diff工作提前到資料層,是不是可以提升性能呢?

帶你重新“玩轉”Flutter

我們嘗試了幾種方法,受制于在Flutter中Dart不能反射,效果不理想,也許後續生态完善之後會有好的解法。

在UI開發中,Flutter一直有一個隐隐的聲音就是動态化,官方好像是“忽略”的,這裡也不談了。

4. 生命周期

Flutter通過StatefulWidget給業務開發提供了基礎的生命周期管理。元件生命周期的設計是随着業務場景的不同而不同的,我自己的了解,設計生命周期要從兩點出發:一是元件從誕生到消亡的時間線,二是元件在場景中所擁有的權益和所承擔的責任。生命周期的擴充原點是StatefulWidget,因業務場景不同而擴充不同,很難展開講。

舉個例子,如果使用原生Flutter開發(或者叫純Flutter開發),StatefulWidget所提供的生命周期是足夠的。但是如果要做混合開發,引入混合棧後,顯然就不夠用了。這時候就需要提供新的元件來擴充生命周期,更好的滿足混合場景的開發。當時我在做混合棧的時候并沒有能了解到這一點。

四. 結語

本篇從分析前端開發需要面對的幾個基礎核心問題入手,結合Flutter帶來的技術工具,嘗試結構化的分析Flutter在業務開發中可能的技術選擇和探索方向。當然,技術同學既要仰望星空,還需要腳下看路,對于Flutter開發中的熱點技術問題,歡迎關注閑魚技術公衆号的其他文章,或者加入我們,和閑魚一起做一點不一樣的技術!