鑒于Flutter高性能渲染和跨平臺的優(yōu)勢,閃點清單在移動端APP上,使用了完整的Flutter框架來開發(fā)竞穷。既然是完整APP,架構(gòu)搭建完全不受歷史Native APP的影響鳞溉,沒有歷史包袱的沉淀瘾带,設(shè)計也能更靈活和健壯。
首先列舉部分閃點清單的業(yè)務(wù)特性(較為通用的業(yè)務(wù)特性):
- 本地有較大數(shù)據(jù)量的清單數(shù)據(jù)熟菲,離線可用看政,未登錄可用朴恳;登錄后需要服務(wù)器數(shù)據(jù)同步
- 狀態(tài)變更場景多,前端狀態(tài)邏輯較為復(fù)雜允蚣,跨頁面于颖、跨組件狀態(tài)更新頻繁
這幾個業(yè)務(wù)點,設(shè)計到的技術(shù)選型有:本地數(shù)據(jù)庫嚷兔、前端狀態(tài)管理森渐,對很多業(yè)務(wù)來說,這幾點都是比較核心的東西冒晰,也是我們今天重點要講的內(nèi)容同衣。
數(shù)據(jù)庫選型
數(shù)據(jù)庫選型,首先要定的壶运,就是選擇數(shù)據(jù)庫類型:關(guān)系型數(shù)據(jù)庫耐齐、非關(guān)系型數(shù)據(jù)庫還是Key/Value存儲。
對于關(guān)系型數(shù)據(jù)庫前弯,可選的有比如:SQLite蚪缀、Core Data、GreenDao等恕出;對于非關(guān)系型數(shù)據(jù)庫询枚,可選有Realm、UnQLite等浙巫;對于Key/Value存儲金蜀,有比如Redis、Berkeley DB的畴、Level DB等渊抄。
由于業(yè)務(wù)形態(tài)具有復(fù)雜的查詢場景,所以首先排除了Key/Value存儲丧裁;然后鑒于業(yè)務(wù)迭代頻繁护桦,數(shù)據(jù)結(jié)構(gòu)變動較大,所以完全的關(guān)系型數(shù)據(jù)庫使用成本會較高煎娇,版本更新時在數(shù)據(jù)兼容和數(shù)據(jù)清洗方面要做較多的工作二庵;所以我們采用了NoSQL數(shù)據(jù)庫,或者支持JSON類型的關(guān)系型數(shù)據(jù)庫缓呛。
Flutter目前在NoSQL上的可選項并不多催享,Realm、UnQLite等均未支持(當(dāng)然可以通過Flutter FFI來封裝給Dart用哟绊,但成本過高)因妙;Flutter的sqflite插件可以較好地支持SQLite,但由于SQLite在3.9以后才支持JSON數(shù)據(jù),考慮到Android版本(Android各版本使用的SQLite版本文檔)兼容問題攀涵,我們并沒有采用sqflite铣耘;我們最終采用的數(shù)據(jù)庫是Sembast,一個還比較小眾汁果,但性能和API建設(shè)都還不錯的NoSQL數(shù)據(jù)庫涡拘。
Sembast介紹
Sembast API很簡潔,但能支持較復(fù)雜的數(shù)據(jù)庫操作据德。
在數(shù)據(jù)查詢上,能夠通過簡單的邏輯API跷车,通過聚合構(gòu)造出復(fù)雜的邏輯查詢語句棘利;數(shù)據(jù)排序?qū)崿F(xiàn)也比較完整,支持多字段排序(但不支持bool類型排序)朽缴;對事務(wù)操作也有支持善玫;支持整型自增key。
Sembast部分API預(yù)覽
var store = intMapStoreFactory.store('animals');
// 事務(wù)處理
await db.transaction((txn) async {
await store.add(txn, {'name': 'fish'});
await store.add(txn, {'name': 'cat'});
await store.add(txn, {'name': 'dog'});
});
// 數(shù)據(jù)查詢
var finder = Finder(
filter: Filter.greaterThan('name', 'cat'),
sortOrders: [SortOrder('name')]);
var records = await store.find(db, finder: finder);
expect(records.length, 2);
expect(records[0]['name'], 'dog');
expect(records[1]['name'], 'fish');
但由于對小眾數(shù)據(jù)庫前途的擔(dān)憂考慮密强,我們設(shè)計了便于遷移的數(shù)據(jù)架構(gòu)茅郎,對數(shù)據(jù)操作層做了一層抽象,后期如果遷移數(shù)據(jù)庫或渤,業(yè)務(wù)層可以完全不需要改動系冗。
狀態(tài)管理選型
狀態(tài)管理,在如今的前端技術(shù)中薪鹦,是非常重要的一環(huán)掌敬,好的狀態(tài)管理框架,可以讓業(yè)務(wù)更好得解耦池磁、簡化組件數(shù)據(jù)通訊成本奔害、大幅提升開發(fā)體驗。我們在狀態(tài)管理選型上花了較多的時間來對比各種方案地熄,比如:provider华临、bloc、redux端考、scoped_model雅潭、mobx、甚至業(yè)界網(wǎng)紅團(tuán)隊的fish-redux跛梗。我們最終采用的是mobx寻馏,關(guān)于各個方案的對比,一篇文章講不完核偿,我們最終選擇mobx诚欠,更多是因為它的API更友好。
Mobx采用注解的方式來定義狀態(tài),并封裝了一個Widget用于Widget的數(shù)據(jù)更新轰绵,學(xué)習(xí)粉寞、使用成本較低。
注解定義
Mobx的注解使用方式左腔,與Web中的Vue非常類似如:
- 使用@observable來注解一個屬性唧垦,表示其需要被監(jiān)聽,Mobx會自動為其添加getter和setter
- 使用@computed來注解一個計算屬性
- 使用@action來注解一個修改store的方法液样,類似于Vuex里的mutations
示例代碼:
import 'package:mobx/mobx.dart';
part 'counter.g.dart';
class Counter = CounterBase with _$Counter;
abstract class CounterBase with Store {
@observable
int value = 0;
@computed
int get allowCount {
return value*2;
}
@action
void increment() {
value++;
}
}
使用Mobx必須的一個步驟振亮,就是前置編譯步驟。我們編寫Mobx的狀態(tài)腳本鞭莽,需要在前置編譯環(huán)節(jié)坊秸,編譯成dart可讀的腳本,為此我們需要執(zhí)行flutter pub run build_runner build
生成.g.dart
為后綴的文件(其實就是將注解轉(zhuǎn)義為getter和setter)澎怒。
UI更新方式
UI更新方式
需要使用Mobx的Widget褒搔,使用Observer包裝一層,這樣其就可以相應(yīng)Mobx的狀態(tài)變化喷面,如:
Observer(
builder: (context) => Text('昵稱: ${userStore.user.username}'),
)
前置編譯腳本
Dart在編譯時和運(yùn)行時均無法支持注解星瘾,所以Mobx使用了前置編譯腳本解析注解動態(tài)生成Dart代碼的方式(Dart官方支持)。Mobx注解方式使用前惧辈,需要為項目添加build_runner
和mobx_codegen
依賴琳状。build_runner為Dart官方提供,用于在構(gòu)建項目之前執(zhí)行特定任務(wù)咬像,任務(wù)配置于依賴庫的build.yaml
腳本中; mobx_codegen
依賴于該build_runner
將注解生成可執(zhí)行的Dart代碼(如添加getter和setter方法)算撮。
結(jié)尾
狀態(tài)管理和數(shù)據(jù)庫,是前端項目基礎(chǔ)框架的重要環(huán)節(jié)县昂,設(shè)計好了可以很好地提升開發(fā)體驗和效率肮柜,降低后續(xù)開發(fā)維護(hù)成本。
講到這里倒彰,還并沒有完成基礎(chǔ)框架的搭建审洞,后面我們會講解更多的Flutter架構(gòu)設(shè)計內(nèi)容,比如:國際化待讳、通知芒澜、分享、UI設(shè)計等等创淡。
持續(xù)分享閃點清單在Flutter上的開發(fā)經(jīng)驗痴晦,閃點清單移動端APP體驗:
硬廣,很硬
閃點清單PC端APP: 業(yè)界唯一一款懸浮清單軟件
閃點清單移動端APP: