背景
為了讓大家能夠了解我們在進行Native轉型Flutter期間遇到的問題以及為什麼要進行這樣的決策,首先介紹一下專案背景。
進行Flutter轉型的專案是公司一個長期迭代的物聯網App,該產品會發布到Android和iOS兩個平臺上,平均每個月都需要釋出一個版本。到目前為止,整個產品已經迭代超過4年,單端程式碼規模幾十萬行,涉及15+個不同的軟體模組庫。
在該產品的整個生命週期中經歷了幾次技術演進過程,從最初的所有業務程式碼在一起的混沌開發狀態到基於Clean Arch模式完成業務分層隔離,不同業務模組拆分為不同的業務庫各自發布版本最終通過一個構建系統完成App打包;從單純的Native程式碼完成業務演進為Native與H5混合型App。
團隊規模從最初的10人左右發展到現在的50+人,包括Android、iOS、H5前端開發工程師。由於開發技術棧分散導致常常出現以下幾個問題:
- Android的已修復的bug在iOS平臺再出現
- 同樣的業務雙平臺實現不一致
- 業務程式碼難以Review
複製程式碼
隨著業務壓力的增加,公司對於團隊交付效率的要求也越來越高。 為了解決上面的現實問題,我們選擇將業務全面從Native開發轉向跨平臺開發技術,從眾多的跨平臺開發技術方案中,我們選擇了Flutter。
為什麼選擇Flutter
為了解決多端業務邏輯一致性、技術開發棧一致性等問題,唯一的選擇就是使用跨平臺開發技術;目前有很多比較成熟跨平臺開發技術,比如React Native、Weex、混合H5開發模式等。由於團隊內部H5開發使用的前端技術棧主要是React,因此我們只對比評估了Flutter和RN,個人覺得寫的比較細緻和全面的具體如下:
- 2019年,Flutter 和 React Native 誰主沉浮 (目前Flutter還不支援熱更新)
- Flutter和RN誰才是更好的跨端開發方案
- 移動端跨平臺開發的深度解析
除了參考網上的相關技術文章外,我們選擇某個比較複雜的業務頁面進行了RN和Flutter的實現對比,最終推動我們確定轉向Flutter的重要原因就在於圖中紅色方框裡的個性化定製的UI控制元件部分的差異:
ReactNative在以下幾個方面存在問題:
- 從維護成本來看
- 由於UI控制元件開發方和使用方屬於兩個不同技術棧,需要維護元件使用文件
- RN的Native控制元件需要在兩個平臺上分別維護
- 元件開發人員需要確保雙平臺一致性
- 從團隊配合來看,UI控制元件開發方和使用方屬於兩個團隊,
- 增加了跨團隊溝通成本
- 業務開發過程中導致兩個團隊出現耦合
- 測試出現問題後,問題定位容易出現扯皮
實現一個業務遇到的問題
當我嘗試實現一個具體業務時,作為一個具有Native經驗的開發者主要遇到以下幾類的問題。我認為這些問題具有共通性,因此將我在解決問題的過程中查詢到的比較好的資料、自己的心得分享給大家,希望能給大家帶來些微幫助。
基礎知識
關於Flutter的環境搭建、基礎知識,推薦大家直接到Flutter中文網進行學習,主要需要學習:
- Dart語言
對於具備一定Android開發經驗的同學來說,Dart語言學習的難度不大;iOS開發的同學,如果學習過swift的話,也能較快的熟悉; - Flutter框架
Flutter採用與前端React類似的響應式程式設計模型,對於Native開發同學來說,難點在於思考問題方式的轉換;具體的差異在下面的響應式程式設計中說明;
App應用框架
在最初閱讀完Flutter中文網的幫助後,我迫不及待想編寫一個“hello world”:
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Text("Hello the flutter");
}
}
複製程式碼
實際上,我並沒有得到預期中的“Hello World”頁面,而是顯示瞭如下的錯誤頁面:
在修改程式碼後class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Text("hello the flutter")
);
}
}
複製程式碼
終於得到了“Hello World”頁面:
經過閱讀MaterialApp的文件發現原來Flutter將App應用框架也抽象成了一個Widget,使用者可以通過Widget屬性配置:- 頁面路由管理器
- 主題相關資料
- 國際化相關資料
- 應用級初始化業務
怪不得在Flutter領域有人感嘆:“萬物皆是Widget!”。
響應式程式設計
在剛剛使用Flutter的時候,困擾我最大的問題就是:什麼是響應式程式設計?為什麼說Flutter的UI框架在誕生之初就是基於響應式設計的?
因此查了很多資料,試圖弄清楚 什麼是響應式程式設計?它與函數語言程式設計,指令式程式設計是什麼關係?這些方式與基於UI的MVC、MVP、MVVM模式是什麼關係? 如果你也遇到類似的問題,強烈推薦仔細閱讀閒魚技術的Flutter React程式設計正規化實踐。
分享以下心得:
- 響應式程式設計的英文是Reactive,不要因為它的中文名字把它與函數語言程式設計、指令式程式設計放在一起比較
- Reactive是一種處理View與Model的方法,與MVC、MVP、MVVM是解決的是一類問題,如下圖
- 在Flutter上進行UI開發時與Android、iOS最大的不同是通過改變資料來改變View,而不是直接設定View;
Flutter不提倡基本不會提供操作View的API,比如我們常見的類似TextView.setText(),Button.setOnClick(); - 當多個頁面均使用Reactive方式時,如何優雅的管理頁面私有資料和應用級資料就成為一個問題,因此狀態管理是使用Flutter完成複雜業務必須要認真考慮的問題;
Native與Flutter混合開發的方式,由於全域性性資料均在Native層面維護,應用級資料管理的問題反而不是那麼突出;
更強大的Dart語法
Dart作為Google新一代開發語言,相比Java和OC,Dart具有更先進的一些語法特性;如何充分發揮語言特性,寫出優雅簡潔的程式碼也是一個挑戰。具體的Dart相關的資料推薦
由於有Java開發經驗,我在閱讀Dart程式碼時基本上都能看懂,除了以下幾點是需要特別注意:
-
可選命名引數與簡化變數賦值
以下程式碼是Flutter的官方程式碼中常見的Widget建構函式,主要使用了Dart的可選命名引數、簡化變數賦值特性,極大的減少了機械編碼的工作量;const Text( this.data, { Key key, this.style, this.strutStyle, this.textAlign, this.textDirection, this.locale, this.softWrap, this.overflow, this.textScaleFactor, this.maxLines, this.semanticsLabel, this.textWidthBasis, }) //具體使用方式 new Text(“文字內容”, textAlign: TextAlign.left, style: TextStyle(color: Colors.black, fontSize: 13)), 複製程式碼
-
?相關的語法糖, 解決語法空問題
參考資料:Dart 到底是不是空安全的- ‘?.’條件成員訪問,可以簡化訪問某個物件屬性或方法時的非空判斷
- ‘??’判空操作符,'??'左邊的值不為空則返回左邊的值
- '??='空感知賦值,當被賦值的變數為null時才被賦值
-
'..' 與 '...' 操作符
- '..'中叫做級聯運算子,主要用於簡化同一個物件上連續呼叫多個函式以及訪問成員變數的程式碼表達,如下:
main() { Student() ..testMethod() ..testMethod1() ..string = "貓了個咪" ..printString(); } 複製程式碼
等效於:
main() { var student = Student(); student.testMethod(); student.testMethod1(); student.string = "貓了個咪"; student.printString(); } 複製程式碼
- '...'是Dart 2.3中新增加的特性,叫做展開運算子,與之一起增加的還有陣列中的‘if’、‘for’表示式;這一優化主要是為了簡化Flutter的Widgets樹編寫方式;
參考:Dart 2.3 釋出,引入優化 UI 構建的新語言特性
-
非同步支援
Flutter在語法中對於非同步函式有非常良好的支援,不像Java還需要引入RxJava等庫;
具體可以參考:Flutter/Dart中的非同步 -
類構造方法
- 命名建構函式,可以為一個類實現多個不同名字的建構函式以便於更清晰的表明你的意圖
- 工廠建構函式,按照一定邏輯定義類構造器返回的具體例項,可以更方便的實現單例模式
待解決問題
從使用Flutter完成簡單業務到上線一個Flutter業務到目前的產品中,還需要解決以下幾個方面的問題:
- 搭建頁面路由機制,支援Native頁面與Flutter頁面的相互路由
- 指定資料共享策略,確保Native與Flutter的業務資料一致性
- 確定Native-Flutter多團隊協同開發模式,解決程式碼庫管理、除錯等問題
- 完成CI改造,混合工程做到持續構建