about 3 years ago

前言

ComponentKit 是 Facebook 團隊開發的 UI Framework
Introducing ComponentKit

ComponentKit gave us performance and reliability gains in News Feed UI Rendering and made the system easier to reason about
Facebook 強調 ComponentKit 改善並強化了 Facbook app 上動態牆的效能,
非常吸引人想去了解這個 framework 究竟有多神奇

開始

Getting Started
在這份文件中,Facebook 大略提到使用 ComponentKit 上的一些細節與概念。

首先,Facebook 使用 Objective-C++ 來實作 ComponentKit,
在文件內有提到為何要使用 Objective-C++,不管原因為何,
對於要使用這個 framework 的開發者而言,首先要做的就是專案內的檔案,也必須跟著是 .mm 檔才行。

再來,由於文件內提到,ComponentKit really shines when used with a UICollectionView.
既然如此,我們就試著用 CompontentKit 來實作 CollectionView。

我們仍然先建立起 UICollectionView 與 UICollectionViewLayout,
在這邊我們其實不用設定 collectionView 的 delegate、dataSource 與 registerCell,
因為當你把 collectionView 給 CKCollectionViewDataSource,
CKCollectionViewDataSource 會用自己定義把這些洗掉,因此設定也沒有什麼意義。

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(100.0, 300.0);
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0.0, 64.0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame)) collectionViewLayout:layout];

CKCollectionViewDataSource 是 ComponentKit 中專為 UICollectionView 設計的元件,
讓你不需要去使用 CKComponentDataSource 去操作更多細節,直接幫你封裝好一個專門的元件,
他扮演的角色跟我們常用的 UICollectionView 或 UITableView 中的 dataSource 類似,
一般的 dataSource 對象是 cell,而這個 dataSource 對象是 component。

在 init 時,帶入的參數比較特別的是 componentProvider,他是一個 protocol,
讓 CKComponentDataSource 可以知道是誰要來負責來實作給予 component 的 class。

CKCollectionViewDataSource *fuckingSource = [[CKCollectionViewDataSource alloc] initWithCollectionView:collectionView supplementaryViewDataSource:nil componentProvider:[self class] context:nil cellConfigurationFunction:nil];

我們可以試試看插入一個 section 跟一個 row,

CKArrayControllerSections sections;
CKArrayControllerInputItems items;
sections.insert(0);
items.insert({0,0}, @"1");
[fuckingSource enqueueChangeset:{sections, items} constrainedSize:{{0,0}, {1000, 1000}}];

在 class 中必須實作 CKComponentProvider 這個 delegate 的 method,
當 CKComponentDataSource 被加入了資料, CKComponentDataSource 會傳一個 model 物件,
必須依照這個 model 給予一個相對應的 CKComponent,而這個 model 就是當時 insert 時帶的 object,
假設我們要在這個 component 內顯示一個 UIView。

+ (CKComponent *)componentForModel:(id<NSObject>)model context:(id<NSObject>)context
{
    return [CKComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100.0, 300.0}];
}

那假設我們要一個紅色背景的 UIView 內有一個綠色背景 UILabel,
label y 軸為 50.0,label 的文字顯示我們的 model,要怎麼描述呢?

+ (CKComponent *)componentForModel:(id<NSObject>)model context:(id<NSObject>)context
{
    return [CKCompositeComponent newWithComponent:[CKStackLayoutComponent newWithView:{[UIView class], {{@selector(setBackgroundColor:), [UIColor redColor]}}} size:{100.0, 300.0} style:{} children:{{[CKInsetComponent newWithInsets:{.top = 50.0, .left = 0.0, .bottom = 0.0, .right = 0.0} component:[CKComponent newWithView:{[UILabel class],{{@selector(setBackgroundColor:), [UIColor greenColor]}, {@selector(setText:), [NSString stringWithFormat:@"%@",model]}}} size:{50.0, 50.0}]]}}]];
}

若不是使用 CollectionView,必須要改用 CKComponentDataSource,
基本上 CKCollectionViewDataSource 只是把 CKComponentDataSource 包起來讓你更容易操作,
因此要怎麼使用 CKComponentDataSource,可以去看看 CKCollectionViewDataSource.mm 如何實作。

寫到這邊,我決定要放棄了,因此直接來講結論。

結論

1. 思考如何設計 CompnentKit 的 UI,而不用實際去實作
呼應文件提到的 A simple analogy is to think of a component as a stencil: a fixed description that can be used to paint a view but that is not a view itself.
從上面的例子可以看到,我們不需實際上去 init、addSubView 或是 setFrame,只需要描述我們所需要的 UI

2. immutable models to immutable components
我覺得這個是 ComponentKit 最主要處理的問題,
以 Facebook 的動態牆來思考的話,在使用者進入動態牆的頁面時,不可能將全部的動態牆訊息一次載入,
必然是分段進行載入,因此儲存訊息的 model 必然一直在更新,這時候畫面必須不斷的跟著 model 更新,
這時候使用者又不斷的在 scroll,畫面應該會相當卡。
我們可以從 Dive Deeper 這裡看出,
modle 與 component 在 background thread 之間的關係,也大概明白為何會這樣設計。

3. 應用與實用
ComponentKit 文件內提到許多優勢與好處,若想做一個新的專案,可以從基底開始就使用 ComponentKit 來實作,
但若要導入至既有專案內,檔案、語法或結構都要調整,成本其實是相當高的。
文件內不斷提到 readable 是透過敘述式的語法,可以讓設計者從 UI code 就可以了解 data model,
但我認為對於已經很習慣將 model 跟 view 切開來實作的設計者,反而對於這樣的 code 會感到很困惑。

4. 是個好範本
CompnentKit 提供蠻多很好的設計理念,enqueue Compnent 的概念或是
immutable model 與 UI 非同步更新部分,有很多可以學習的地方。

← Python:Amazon s3 iOS : Audio Streaming ( Audio Queue ) →