前言
上一篇關於Cordova的文章簡單介紹了Cordova的主要實現程式碼和Cordova對web容器和外掛相關的處理,本篇將著重介紹Cordova在平臺化應用中的深度定製化。目前原生開發者持續不斷的在受到前端開發者的衝擊,前有Cordova相關的hybrid類APP,後有weex、RN等框架的出現,當然我覺得weex、RN類技術是更應該被提倡的,因為他們兼顧了JS開發,原生渲染的特點。但是Cordova類框架也有它們的優點,微應用完全可以使用任何前端框架來編寫,並且可控性強,雖然在渲染層面和weex、RN有一定的差距,但是目前我們使用react.js這種單頁面應用配合離線包策略,也一定程度上降低了它們在渲染層的差距,有效快速的解決發版週期之間的矛盾、跨平臺開發、實時釋出等一些問題,而且有效地保證了釋出質量和線上問題的及時修復。
框架對外提供
1.基於Cordova互動外掛化,打包提供給H5使用,H5只需要引入一個檔案就可以使用native提供的各種原生功能,包括但不限於fetch,map,IO操作,檔案操作等。
2.提供“離線包”策略,提升渲染速度和避免不必要的網路請求,進行構建微應用。
3.提供三方授權服務,進行微應用的單點登入授權功能。
web容器優化方案
首先,手機要通過無線網路協議,從基站獲得無線鏈路分配,才能跟網路進行通訊。 無線網路基站、基站控制器這方面,會給手機進行訊號的分配,已完成手機連線和互動。 獲得無線鏈路後,會進行網路附著、加密、鑑權,核心網路會檢查你是不是可以連線在這個網路上,是否開通套餐,是不是漫遊等。核心網路有SGSN和GGSN,在這一步完成無線網路協議和有線乙太網的協議轉換。 再下一步,核心網路會給你進行APN選擇、IP分配、啟動計費。 再往下面,才是傳統網路的步驟:DNS查詢、響應,建立TCP連結,HTTP GET,RTTP RESPONSE 200 OK,HTTP RESPONSE DATA,LAST HTTP RESPONSE DATA,開始UI展現。可見,通過運營商的網路上網,情況比較複雜,經過的節點太多;運營商的網路訊號強度變化頻繁,連線狀態切換快;網路延遲高、丟包率高;網路建立連線的代價高,傳輸速度快慢不等(從2G到4G,相差很大)。
1.壓縮html, js, css
壓縮程式碼,尤其是js和css資源,壓縮後的大小可以降低至原來的1/3以下,有效節約流量。
2.避免30*/40*/50*的http status
200是一個正常的response,我們在瀏覽器中開啟一個網頁(後面會講如何針對移動端進行除錯),還會看到304,即命中瀏覽器快取。這兩種狀態是正常的http status。
302、301跳轉是常見的跳轉,尤其前一種,在我們進行鑑權的時候有時會用到,但這個做法要儘可能地優化,一個頁面訪問,最多隻進行一次302跳轉即可,切忌頻繁地跳轉。
404、500,我們對自己開發的程式碼比較注意,一般不會發生,但是有的時候,載入第三方庫,尤其是第三方庫中有自己load元件的操作,這時,404和500錯誤可能會在你不知不覺的時候發生。
3.資源的版本更新
業務的js和css可能經常會有更新,如果命中瀏覽器快取,可能會讓一些新的特性不能及時展現,甚至可能導致邏輯上的衝突。因此對於這些js、css的資源引入,最好用版本號或者更新時間來作為字尾,這樣的話,字尾不變,命中快取;字尾改變,瀏覽器自動更新最新的程式碼。
4.介面資料快取
快取介面資料,在一些資料新舊敏感性不高的場景下很有作用,在非首次載入資料時候優先使用上次請求來的快取資料,可以讓頁面更加快速地渲染出來,而不用等待一個新的http請求結束之後再渲染。
5.單頁應用
釘釘的審批微應用,使用的就是單頁架構。在這種架構下,基本不存在頁面跳轉的等待時間,只需要執行js邏輯觸發介面變化,最多進行一次網路請求,獲得服務端資料,其他資源均不需要再次請求。
6.資源離線
再快的網路互動,畢竟也是跨越了數個網路節點,因此一張圖片、一個js,優化到了極致,也照樣可能需要幾百毫秒的時間來獲得。因此想要打破這個極限,就要使用資源離線的策略。網頁程式碼裡面載入網路資源的需求,就變成了直接載入本地檔案,速度自然得到再一次巨大的提升。
7.預載入機制
有時,我們能夠通過使用者的行為統計,預判出使用者下一步可能進行的操作。假設,我們統計出來針對某個微應用,使用者首頁渲染完成之後,大部分會點選列表中的第一個專案檢視詳情。那麼在首頁渲染完成之後,我們就可以先預先載入第一個專案的部分內容,那麼針對這部分使用者,他們實際點選之後,立即就能看到新的頁面中的內容。
在平臺內H5頁面是怎麼載入JS外掛的:
起初我們的H5程式碼都是隨著應用一同釋出的,這樣不同平臺的應用自然會將相應的cordova.js打包到應用中,載入相應的cordova_plugin.js,初始化js外掛。由於應用逐漸走向平臺化發展,那麼我們的問題來了,怎樣將我們的外掛給其他三方應用使用?讓其他系統可以方便的整合和使用?基於這樣的需求,我們引入了兩個自定義的js檔案,cordova-dynamic-loading.js
和pm-cordova.js
。cordova-dynamic-loading.js需要在業務端程式碼前引入,用於檢查cordova環境,並且根據客戶端系統,初始化不同的外掛。而pm-cordova.js是js-bridge介面封裝庫。
架構圖大致如下:
-
1.H5應用層:目前基於react.js構建的單頁面應用,取消頁面跳轉帶來的不必要的網路載入。
-
2.外掛API介面層:平臺對外輸出外掛介面。
-
3.平臺標識:實際上是為瀏覽器設定的唯一標識,區分不同平臺,便於載入不同的js外掛使用。
-
4.動態載入:基於不同平臺的userAgent,動態載入相應平臺的js外掛,其中cordova-dynamic-loading.js和cordova.js為具體實現程式碼。cordova.js還承擔著原生與js互動對外掛id,引數的傳遞,回撥的儲存等相關工作。
-
5.平臺:不同平臺提供相應平臺的js外掛,供cordova.js載入。
具體這兩個檔案的實現程式碼由前端大神編寫的,這裡就不粘了,作為一個菜鳥iOS程式設計師,前端目前還尚處於學習中。當然框架內還提供了自定義的pm-cordova-mock.js
可選引入,是pm-cordova.js的web端mock版本,本地開發業務進行測試時可以引入這個檔案替換pm-cordova.js。除此之外,平臺內也同樣內建了一套相關程式碼,三方應用完全可以不必整合外掛相關程式碼也可以與平臺進行互動。
定製userAgent
UIWebView沒有提供設定UserAgent的介面,但是可以通過cordova框架內CDVUserAgentUtil
類,設定userAgent。
@interface CDVUserAgentUtil : NSObject
+ (NSString*)originalUserAgent;
+ (void)acquireLock:(void (^)(NSInteger lockToken))block;
+ (void)releaseLock:(NSInteger*)lockToken;
+ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken;
@end
複製程式碼
+ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken
{
//斷言
NSAssert(gCurrentLockToken == lockToken, @"Got token %ld, expected %ld", (long)lockToken, (long)gCurrentLockToken);
//設定userAgent
//必須在例項化UIWebView之前設定UserAgent。
//它是按例項化讀取的,所以它不會影響以前建立的檢視。
//除外!當一個PDF被載入時,所有當前活躍的uiwebview重新載入它們
//來自NSUserDefaults的User-Agent在PDF bah的DidFinishLoad之後的一段時間!
NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:value, @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dict];
}
複製程式碼
通過設定NSUserDefaults中UserAgent的值來修改,但是這種設定方法有一個限制,需要在UIWebView的loadRequest之前呼叫才能生效(載入PDF比較特殊)。這是Cordova原始碼中關於這個問題的描述。
- (NSString*)userAgent
{
if (_userAgent != nil) {
return _userAgent;
}
//省略部分原始碼
_userAgent = [NSString stringWithFormat:@"%@ %@", _userAgent, @"[AWV/v1.0.0;AD/iOS]"];
return _userAgent;
}
複製程式碼
Cordova框架內提供對瀏覽器userAgent的設定相關程式碼,平臺內對userAgent做了一些定製化的內容,比如自定義了userAgent引數,這裡的userAgent實際上就是提供給上面cordova-dynamic-loading.js
檔案進行解析使用的。實際上通過Cordova原始碼可以看到還有一些包括對userAgent的釋放等其他操作,這裡不做具體分析。關於CDVUserAgentUtil可以參考Cordova原始碼解析(二)- 自定義UserAgent。
實際上平臺在js部分深度整合做了什麼?
-
1.對三方系統提供
cordova-dynamic-loading.js
檔案,用於檢查cordova環境。 -
2.提供
pm-cordova.js
檔案用與對js橋封裝。 -
3.提供
pmCordova.platform.ready
(實際上是對外提供的一個外掛)函式,由於cordova外掛非同步進行載入和初始化,用於保證安全呼叫。 -
4.提供高階配置,三方系統可按照自己的需求對外掛進行配置,減少需要載入的plugin,提高效能。
-
5.提供pm-cordova-mock.js檔案,用於本地開發業務進行測試使用。
提供“離線包”策略,進行構建微應用
為什麼要提供離線包,前言也有說明,這裡再詳細說一下:
-
1.儘量使容器內H5接近native體驗,將HTML,JabaScript,CSS壓縮為離線包動態下發。
-
2.減少網路環境對H5應用的影響。
-
3.提升開啟H5的速度,減少白屏渲染時間。
-
4.實現微應用的動態更新。
通過應用管理平臺,釋出應用時會配置相應的版本號等資訊,客戶端啟動後會後臺比對版本等資訊,檢查應用是否需要更新等。關於離線包策略,為了防止使用者阻塞,伺服器會部署一套完全一樣的程式碼,在離線包沒有下載好的情況下,客戶端會先去載入服務端的程式碼,待下次進入該應用的時候載入本地離線包內的程式碼,這一塊的處理和前幾天看到支付寶移動端動態化方案實踐有些相似。
離線包統一由打包平臺一鍵打包釋出,從程式碼獲取到壓縮,到生成zip包,再到釋出一氣呵成,並相應的對版本號進行遞增,目前平臺客戶端支援增量更新和全量更新,增量更新可以更節省流量,更新速度更快。全量更新可以保證版本更穩定,安全性更高。基於此,平臺客戶端實現了應用的動態釋出與更新以及線上bug的修復,不需要再重新打包釋出。
那麼離線包功能客戶端做了什麼
- 1.提供離線包安裝路徑:
客戶端通過不同應用的應用id擴充套件不同的應用安裝路徑,用於應用的安裝和開啟。 實際上在客戶端最終會有這樣的目錄結構:
- 2.改造Cordova預設載入路徑,指定為上圖中的目標路徑:
- (NSURL*)appUrl
{
//省略了一部分程式碼
NSURL* appURL = nil;
if ([self.startPage rangeOfString:@"://"].location != NSNotFound) {
} else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) {
} else if([self.wwwFolderName hasSuffix:@".bundle"]){
} else {
// CB-3005 strip parameters from start page to check if page exists in resources
NSURL* startURL = [NSURL URLWithString:self.startPage];
NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]];
}
return appURL;
}
複製程式碼
主要做的工作全部在NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]];
內實現,這行程式碼會生成我們預設好的目錄檔案路徑。
Cordova內部實際上預設載入的路徑為bundle,需要修改為我們指定的沙盒路徑然後去載入沙盒裡面相應的微應用進行UI展示。
提供三方授權服務,進行微應用的單點登入
應用有了,也提供了離線包策略,那麼三方應用怎麼登入平臺?基於這個需求我們提供了Oauth2服務,進行三方登入授權。這部分不在web容器範疇內,後續會具體分析。
注:以上工作的實現以及web容器優化方案來源於民生辦公開發團隊。