国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術文章
文章詳情頁

58 同城 iOS 客戶端搜索模塊組件化實踐

瀏覽:3日期:2022-09-17 11:15:30
引言

58 同城的搜索功能支撐了近一半的用戶流量,所以搜索是一個很重要的模塊。眾所周知,iPhone 的搜索是通過 Spotlight 來實現的,那么在 App 內部是如何實現搜索呢?首先了解一下 58 同城的搜索需求:

58 同城首頁,提供搜索功能,稱為全站搜。 58 同城有二手物品、房產、二手車、招聘、黃頁幾大業務線,這是粗粒度的業務線。細分一下,二手可以拆分出二手物品、寵物等類別;房產拆分出租房、二手房等類別;招聘拆分出全職招聘、兼職等類別;黃頁拆分出家政、本地服務等類別。拆分出的這些較細的類別的頁面稱之為大類頁,這些大類頁也提供搜索功能,稱為大類搜。 大類頁提供更細粒度的類別,如進入二手房大類頁后會看到二手房、新房、商鋪、廠房等入口,再次進入后是列表頁,這些列表頁也提供搜索功能,稱為列表搜。

圖 1 是舊的搜索框架,雖然看上去比較清晰,但實際上存在著很多問題,比如代碼冗余、耦合度高、不易復用等。這些也是一些大型模塊經過多次升級,到了后期經常存在的問題。接下來具體問題具體分析。

存在的問題

從最開始的 1.0 版本,就在首頁實現了搜索功能。隨著業務的擴展,58 的業務線也在逐漸成型和完善,每個業務線的大類頁接入搜索功能也存在先后。業務線內部的列表頁,更是多種多樣,比如 Native 的類別頁、Web 列表頁,還有特殊的列表頁(如簡歷庫列表頁、地圖搜房頁等),這些列表頁后來也都一一實現了搜索功能。不過也正是因為實現的時間有先后,逐漸積累產生了一些歷史遺留問題。

代碼冗余

不同的業務頁面對搜索功能的支持有先后之別,后實現搜索的頁面都是先拷貝一份先實現搜索的代碼,然后把其中的業務代碼刪除,加入自己的。舊版的業務入口頁面各自實現了一套搜索邏輯(如圖 2 所示),重復的代碼超過一萬行。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 2 業務頁面各自實現搜索頁面

耦合度高

從圖 1 可以看出,搜索頁面是由業務入口頁面管理和加載的。搜索頁面要處理數據,包括熱詞和搜索歷史的本地獲取、服務器獲?。灰幚砭W絡請求,包括關鍵詞的聯想請求、搜索請求;還要實現視圖的協議方法。這些大量的邏輯都是在業務頁面文件中實現的。

在代碼管理上,盡管已經把搜索相關的代碼剝離出來,單獨放到了一個 Category 類別文件中,但實際上還是無法避免地跟業務頁面邏輯耦合在一起。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 1 舊版搜索框架

文件級別。業務頁面文件中,既有業務方法,又有搜索相關的方法; 方法級別。在同一個方法中,既有業務邏輯,又有搜索邏輯。這是更為嚴重的耦合。 搜索頁面無法復用

因為搜索頁面與業務頁面耦合度高,所以業務入口頁面無法復用以前的搜索頁面,只能各自實現。相反的,越來越多的業務入口頁面不再考慮搜索頁面的復用性,只考慮自己獨自實現,導致搜索頁面越來越多,比以前更加難以復用和移植。

另一個無法復用的原因來自于搜索頁面自身的定制化嚴重。搜索頁面需要清楚地知道是否存在熱詞、搜索歷史、聯想結果等,然后定制顯示視圖,如圖 3 所示是兩個搜索頁面的簡化類圖。搜索頁面的列表協議方法均直接訪問了搜索頁面的屬性,但其屬性是與搜索業務直接相關的。全站搜中沒有城市業務,而城市選擇頁搜頁面也沒有熱詞等業務,這樣的定制導致搜索頁面無法復用。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 3 兩個搜索頁面的簡化類圖

業務功能接入成本高

業務功能接入成本高,表現在以下兩個方面:

代碼復用度低,開發成本高:業務頁面想實現搜索頁面成本高。前面已經介紹過了,搜索頁面無法復用,業務頁面需要自行實現一套搜索頁面。 搜索頁面與搜索結果頁耦合性高:從搜索頁面到搜索結果頁之間的跳轉只處理了固定頁面的跳轉。如果是列表頁面接入搜索頁面,只需要基于當前頁面刷新即可。而全站搜和大類搜是最靈活的,根據搜索詞可以跳轉到所有業務線的落地頁。但是 JumpManager 模塊只處理了固定的頁面跳轉(如圖 1,只有搜索類別頁、搜索結果列表頁),假如業務線想搜索后跳轉到一個自定義的頁面(如搜索“拼車”),JumpManager 是無法實現的。

這種只能跳轉到固定、有限頁面的跳轉方式,限制了快速變化的業務需求。如果業務功能要接入一個新的跳轉目標頁面,需要發版才能實現,成本很高。

新搜索框架設計

針對上面的歷史遺留問題,重新設計了搜索框架:

把搜索頁面從業務入口頁面中解耦出來,降低了耦合度; 實現了路由中心,讓 App 內頁面的跳轉變得簡單,降低了業務線接入成本; 實現了搜索頁面的組件化,提高了搜索頁面的復用性,減少了代碼冗余。

如圖 4 所示是新搜索框架圖。整體來說是從三個層級實現了搜索模塊的組件化。最外層,通過路由中心,業務入口可以跳轉到搜索頁面,搜索頁面可以跳轉到更多的結果頁面;中間層,搜索頁面內部可以配置搜索框組件和語音組件;最內層是 UITableView 內部的組件化實現,列表中的元素根據數據可以靈活、動態地展示任意組合的樣式,消去了邏輯判斷和業務依賴。下面詳細展開說明新框架。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 4 新版搜索框架

搜索入口解耦

以前的搜索頁面是在業務頁面的視圖控制器內加載的,搜索頁面是一個 UIView,是業務頁面的一個子視圖。業務頁面除了處理自身業務邏輯,還需要實現搜索頁面的大量邏輯,導致視圖控制器動輒幾千行,難以維護,代碼可讀性較差,視圖控制器邏輯太多、過于復雜。

新框架中,對搜索入口進行了解耦,方法是使用假搜輸入框作為搜索入口,如圖 5 所示的視圖層級,用戶能看到搜索框,也可以點擊搜索框,但是無法輸入字符,因為搜索框上面覆蓋了一層透明的 UIButton 按鈕。當用戶點擊輸入框時,其實是觸發了 UIButton 的 Target 方法,然后通過路由中心跳轉到搜索頁面。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 5 搜索入口解耦方式

搜索頁面解耦

以前的搜索頁面,數據和 UI 顯示是耦合在一起的。UI 頁面直接訪問數據屬性,因為需要了解顯示的是什么數據模型、數據模型的 Class。其他業務頁面很難去復用這樣的搜索頁面。

新搜索框架中,把搜索頁面劃分為兩層,上層是數據層,是真正與業務有關的子模塊;下層是組件層,與業務無關,是可以復用的模塊。

數據層。每個業務頁面需要的搜索功能是不用的,區別在于 UI 展示的數據不相同。數據層處理與數據有關的搜索業務邏輯,如數據的獲取、緩存、網絡請求等,除此之外還需要把數據組裝到一個數組中。 組件層。搜索頁面的組件化分為兩層,外層是搜索框組件、語音組件的可配置化,內層是 UITableView 內部的組件化。組件化模塊不需要了解數據的細節,只需要拿到數據層傳來的數據,然后轉化成組件來顯示。

圖 6 是全站搜和城市選擇頁面的搜索頁面,兩個頁面的數據層處理各自的搜索邏輯,但是公用組件層,當組件顯示到列表中的時候,列表不需要知道 Cell 的具體類型,只當作基類類型即可。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 6 搜索頁面通過組件化解耦

路由中心

舊的搜索框架,搜索完成后的跳轉通過 JumpManager 實現,且只能跳轉到有限的兩種頁面。雖然頁面可以根據接口展現不同內容,但隨著業務發展,舊搜索框架已無法滿足。

比如,隨著 iOS 系統的升級,App 開始支持 WKWebView,但是因為使用 UIWebView 頁面的業務線還很多,所以需要長時間保持兩者共存的方式。搜索關鍵詞進入 Web 類型的結果頁面時,如何控制 App 進入的是 WKWebView 類型的頁面還是 UIWebView 類型的頁面?

最初可能通過判斷參數來實現。Server 返回的結果中有一個參數 webType,當值為 WKWebView 時采用 WKWebView 類型,否則采用 UIWebView 類型。但是當隨著業務的增長,將會充斥著大量 if/else 的代碼,各種問題接踵而至。

最終我們決定采用更加靈活的方式,即通過路由中心來實現。路由中心不但解決了搜索框架中頁面跳轉的難題,其他業務需求在頁面跳轉時遇到的問題也得以解決。

設計目標

使用簡單。路由中心對使用方而言是一個黑盒子,使用方不需要關心如何實現;

支持多種應用場景。以圖 7 中的 5 種場景,路由中心都可以處理,而且可以跳轉到右側的四種頁面中的任意一個頁面;

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 7 路由中心設計目標

穩定、不輕易變化。因為需要支持 App 中所有頁面的跳轉,如果路由中心不穩定,將會造成很嚴重的后果;

擴展簡單,易維護。業務發展很快,增加頁面是常見的事,需要路由中心很容易實現對新頁面的擴展。

如圖 8 所示即是路由中心的設計圖。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 8 路由中心框架

路由入口

提供了兩類路由入口方法,一類是針對新數據協議的 Scheme URL 路徑類型的參數,另一類是針對舊數據協議的參數,如 Server 傳送來的數據中還有很多在使用字典類型舊協議數據。

協議數據轉換層

不同的路由入口,傳入的數據格式不一樣,如果不進行統一,后面的處理將會有兩套邏輯,出現多個 if/else 的分支代碼,代碼冗余、升級維護麻煩。根據當前的使用頻率和未來的發展方向,最終選擇是把舊協議數據轉換成新協議數據。

新協議規則

新舊協議中包含的字段是一樣的,只是舊的跳轉協議中,參數被放在了字典中,而新跳轉協議中,參數是在一個長字符串中。為了把舊的跳轉協議轉換成新協議數據,制定了新跳轉協議規則格式:

其中:

wbscheme:是 scheme,用于協議區分。外部調起時,區分是不是 58 的交互協議; router:authority,用于業務區分。區分是不是跳起協議; pagetype:屬于 URL 的 Path,用于區分頁面類別,如首頁、列表頁、詳情頁等; tradeline:屬于 URL 的 Path,用于區分業務線,如二手業務線,房產業務線,招聘業務線等等; otherparams=JsonString:query 參數,表示跳轉時需要攜帶的參數; 可以擴展其他字端,以解決跳轉時頁面關閉、是否登錄等問題。

制定了協議規則之后,就可以把舊協議數據與新協議字段相對應了。數據轉換工作是由轉換器完成的。

業務線分發轉換器

前面已經介紹了新舊跳轉協議的參數對應關系,現在需要一個轉換器把舊協議數據轉換成新跳轉協議。但是因為各業務線、主 App 主業務、其他創新業務等在跳轉時,Server 傳入的參數、需要的參數可能都不一樣,所以需要多個轉換器,根據業務線(trandline 參數),我們制定了如圖 9 所示的多個轉換器。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 9 業務線分發轉換器流程

每一個轉換器都是一個單例,并且都實現一個 -(NSString *)dispatchActionData:(NSDictionary *)aJsonDic 方法,作用是把字典類型的舊協議數據轉換為字符串類型的新協議數據。

根據 tradeline 參數,就可以在轉換器映射表中得到其 ClassName,然后通過轉換器調用 dispatchAction:方法,即可完成轉換。

注冊表

前面已經看到了跳轉協議的數據格式,其中兩個參數最重要:tradeline 和 pagetype,因為需要兩個參數來定位一個視圖控制器。

在同一個業務線的注冊表中,key 與視圖控制器唯一對應。不同的業務線,key 值可以重復。圖 10 分別是黃頁業務線和二手車業務線的注冊表,其中 key 表示 pagetype,value 是視圖控制器的 Class Name。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 10 注冊表文件內容

跳轉管理

解析跳轉協議

前面已經統一了跳轉協議,所以在這里只需要解析一種格式的數據即可。解析后可以得到 tradeline 參數、pagetype 參數以及 param 字典。

目標頁面管理

通過注冊表可以基于 tradeline 參數和 pagetype 參數來定位一個唯一的視圖控制器。所以在解析完跳轉協議后,可以利用得到的 Class Name 通過運行時方法創建目標頁面,最后把 param 參數傳遞給目標頁面。

跳轉控制

跳轉控制可以通過 Native 代碼或者 Server 來控制,如果未指定,則采用簡單的 Push 方式跳轉。路由中心支持以下 6 種方式:

簡單的 Push 跳轉到目標頁: 跳轉到目標頁之前 pop 到上級頁面; 跳轉到目標頁之前 PopToRootViewController; 通過 Present 的方式呈現目標頁; 跳轉前先登錄,登錄成功之后才能跳轉; 跳轉過程中沒有動畫。

這 6 種方式的跳轉都可以在跳轉協議中增加參數來控制,實現了跳轉方式的多樣性和靈活性。

視圖控制器

視圖控制器協議

在通過 Class Name 創建對象后,為了更統一地創建視圖控制器、給視圖控制器對象賦值,聲明了一個協議。協議只有一個方法,注冊表中的所有視圖控制器都需要實現這個方法。

視圖控制器擴展

通過注冊表可以通過 tradeline 參數和 pagetype 參數來定位一個唯一的視圖控制器。也就是說所有的目標頁面都有屬于自己的 tradeline 參數、pagetype 參數。無法給所有的目標頁面增加這兩個屬性,也不能讓所有的目標頁面擁有同一個基類,更好的方式是增加一個 UIViewController 的擴展。

擴展中除了動態增加 tradeline、pagetype 兩個屬性外,還加入了跳轉控制的中提及的一些參數作為屬性,方便跳轉控制。

搜索頁面組件化

下面分別說明搜索頁面內部組件化的兩層。

搜索框組件、語音組件可配置

搜索頁面中沒有使用系統的導航欄,是為了更好地定制導航欄位置的搜索框視圖。搜索頁面把搜索框視圖組裝成一個組件,可以根據需求進行靈活配置。比如 58 同城 App 的 7.12.0 版本,全站搜的搜索框增加了一個前置類別選擇框,這是一個新的搜索框組件,只需要在全站搜搜索頁面替換搜索框組件即可,而不需要修改搜索頁面的其他代碼,影響最小。

語音組件也是可以配置的,如果某些業務頁面的搜索頁面不需要,則可以不顯示該組件。

這兩個組件的配置都是通過數據層根據業務來實現的。

下面著重說明 UITableView 內部的組件化。

數據模型適配器

數據模型適配器的作用是把原始數據轉換成與業務有關的數據模型。原始數據可能來自于 Server,也可能是來自于本地緩存。原始數據一般不外乎 NSDictionary、NSArray,為了更好地操作數據,需要轉換成已有的數據模型。

在搜索頁面實現組件化,數據就是 Native 拼接的。比如在全站搜、大類搜中,就是把熱詞、搜索歷史拼接為一個數組,然后交給數據模型工廠處理。數據模型工廠解析原始數據,然后輸出數據 model 數組。

如圖 11 是全站搜的數據模型適配器的輸入和輸出,輸入的是熱詞和搜索歷史數據,而經適配器轉換之后,數據被轉換為 {key : model} 字典組成的數組。保留 key 值,是為了使用 key 經過視圖模型注冊表得到對應 cell 的 Class Name,而 model 對象則是用來為 cell 賦值的。下面詳細解析說明。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 11 數據模型適配器的輸入和輸出

原始數據

傳入的原始數據是一個數組,數組中的元素可能是數組,也可能是字典,如圖 12 所示,左側原始數組,都是字典形式,有 10 個字典,那么最后展現在 UITableView 中,section 數目為 1,row 數目為 10;而右側原始數組中又包含了兩個數組,說明 UITableView 中會有 2 個 section。其中第一個子數組只有 1 個元素,第二個子數組有 4 個元素,說明 UITableView 的兩個 section 分別會有 1 個和 4 個 row。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 12 兩個原始數據

原始數組中,還有一個很重要的參數是字典的 key 值,比如右側原始數組中有一個 key:hotword,在數據模型映射表(如圖 13 所示)中會存在對應這個 key 的一個數據 model:WBSearchHotWordModel。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 13 數據模型注冊表

數據模型注冊表

數據模型注冊表是一個字典,在解析原始數據中,通過 key 值可以找到對應的數據 model 的 Class Name,圖 13 是搜索模塊的數據模型注冊表。

數據模型池

數據模型池不是一個類、文件、模塊,而是多個數據 model 的集合。數據模型池至少包含了數據模型映射表中所需要的數據模型。

數據模型除了聲明一些業務需要的屬性之外,還需要實現一個解析方法 - (void)generateModelWithOriginalDict:(NSDictionary *)dict。

數據模型轉換

數據模型轉換過程如下:

遍歷原始數據數組,取出字典中的 key 值; 通過數據模型映射表,找到 key 對應的 Class Name; 創建數據模型 Class Name 的實例對象 model; 利用數據模型實現的 generateModelWithOriginalDict:傳入數據; 把{key : model}這個字段作為元素加入到新的結果數組中; 遍歷完成,返回新的結果數組。

視圖模型適配器

數據是為視圖服務的,得到數據模型數組后,就可以在頁面展示了。組件是在 UITableView 中展示,所以大部分情況視圖都是 UITableViewCell,如果視圖工廠得到的視圖不是 UITableViewCell,而是 UIView,則工廠會把 UIView 封裝到 UITableViewCell 中。

視圖模型注冊表

視圖模型注冊表是一個字典,前面已經得到了數據模型數組,數組中每個元素都是一個{key : model}字典,通過 key 值可以找到對應的視圖模型的 Class Name,圖 14 是搜索模塊的視圖模型注冊表。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 14 視圖模型注冊表

視圖模型池

視圖模型池不是一個類、文件、模塊,而是多個視圖的集合。視圖模型池至少包含了視圖模型映射表中所需要的視圖模型。

視圖模型除了聲明一些業務需要的屬性之外,還需要實現一個方法 - (void)configSubViewsWithModel:(id)model。對外暴露的方法只有一個,但實現上會根據 model 的具體數據類型,進行不同的處理。

組件模型轉換

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 15 組件生產全過程

組件模型轉換過程就是通過數據 key 值從視圖模型池中查詢模型 Class Name 的過程。如圖 15 中的前 3 個步驟:

resultSearchList 是數據模型工廠的輸出結果,其中每個元素都是一個{key : model}字典; 獲取到 indexPath.row 位置的元素,解析得到 key 和 model 數據; 通過 key 值,通過視圖模型映射表得到視圖的 Class Name。

組件生產

組件的生產過程就是通過數據模型適配器、視圖摸適配器,得到視圖的過程。

完成數據模型適配器的工作后,在 UITableView 的協議方法 tableView:cellForRowAtIndexPath:方法中實現視圖的生成,如圖 18 中的后面三個步驟:

根據 Class Name,生成視圖對象,并且利用視圖方法配置數據; 假如視圖是 UITableViewCell 類型,則直接返回視圖對象; 否則如果視圖只是 UIView 類型,則把 view 加載到 UITableViewCell 中,然后返回 cell 實例。

如此,便生產出配置了數據的組件。

組件池

組件池是按照功能劃分的,在搜索框架中,組件池中的組件是所有搜索頁面可能使用的組件。其中每一個組件不但包括數據模型、視圖模型,還包括注冊表和適配器中的處理。如圖 16 所示,前面的介紹從按照模塊劃分的,而每一個紅框都是一個組件,如熱詞組件。

58 同城 iOS 客戶端搜索模塊組件化實踐

圖 16 組件池

增加組件

隨著業務發展和需求的增加,不可避免會需要增加一些新的組件。原則是能復用盡量復用,不能復用也不必要把組件做的太耦合。

首先按照接口數據,創建一個新的數據 model,包括屬性和解析方法 generateModelWithOriginalDict:; 在數據模型映射表中增加一個新的鍵值對,key 值不要重復,value 就是 model 的 Class Name; 根據 UI 圖開發新的視圖模型,并且完成對數據的配置方法 configSubViewsWithModel: 在視圖模型映射表中增加一個新的鍵值對,key 值與 2 相同,value 就是 3 中新建的視圖模型的 Class Name。

如此,便完成了組件的增加。之后可以根據數據的不同配置,讓組件可以任意的排列組合。

總結

搜索框架的優化是一個持續的過程。當舊的框架無法滿足業務的快速發展時,需要對搜索框架進行大規模的重構,來解決舊代碼冗余、不易復用、不易移植、擴展難的問題;大部分情況下,是進行小規模的優化、擴展以滿足需求的迭代。由此推廣到其他的模塊,其經驗是通的。

模塊/框架系統化設計。綜合考慮各種可能會接入的業務以及當前業務的擴展。比如搜素模塊從剛開始的一個業務線接入及定制化到多個業務線的接入及定制化,所帶來系統的復雜性會有根本變化。設計的時候一定不要脫離具體的業務和問題,以解決具體的問題為出發點。

善用組件化。頁面是一個盒子,組件是盒子里面的擺放的物品,數據指明把哪些物品如何擺放。人操作數據,每個人都可以在盒子里擺出任意排列的物品。所以數據是業務有關的,組件是通用的、可復用的、低耦合的。

及時優化和重構。不要把問題拖到最后,否則小范圍的修改可能會產生大量的問題,問題會爆炸性地爆發,不得不做大規模重構。

來自:http://blog.csdn.net/csdnnews/article/details/78088447

標簽: IOS
相關文章:
主站蜘蛛池模板: 亚洲韩精品欧美一区二区三区 | 男人桶女人逼 | 国产成年人在线观看 | 欧美性色黄大片一级毛片视频 | 亚洲国产一区在线精选 | 国产不卡精品一区二区三区 | 欧美日韩在线播一区二区三区 | 亚洲爽视频 | 久久成人免费大片 | 国内国外精品一区二区 | 国产一区二区三区四区在线观看 | 国产日本在线 | 日韩黄在线观看免费视频 | 女人张开腿男人捅 | 91情侣高清精品国产 | 欧美成人在线免费 | 手机看片国产免费永久 | 日韩欧美视频一区二区在线观看 | 欧美高清在线 | 中国一级特黄视频 | 看性过程三级视频在线观看 | 成人国产一区二区三区精品 | 免费观看成人www精品视频在线 | 国产精品一区二区三区免费 | 午夜宅宅宅影院在线观看 | 色天天躁夜夜躁天干天干 | 欧美一级特黄aa大片在线观看免费 | 国产精品99久久久久久人 | 国产一级aaa全黄毛片 | 全午夜免费一级毛片 | 免费三级网址 | 国产亚洲精品国产第一 | 91精品观看91久久久久久 | 夜鲁夜鲁夜鲁在线观看福利 | 毛片视频在线免费观看 | 欧美国产在线视频 | 精品国产欧美一区二区五十路 | 99视频在线免费观看 | 亚洲欧美视频在线播放 | 黄色片日本人 | 成人福利网站在线看视频 |