iOS H5容器的一些探究(一):UIWebView和WKWebView的比較和選擇

一吉嫩、Native開發(fā)中為什么需要H5容器

Native開發(fā)原生應用是手機操作系統(tǒng)廠商(目前主要是蘋果的iOS和google的Android)對外界提供的標準化的開發(fā)模式,他們對于native開發(fā)提供了一套標準化實現和優(yōu)化方案浪谴。但是他們存在一些硬傷,比如App的發(fā)版周期偏長、有時無法跟上產品的更新節(jié)奏柑蛇;靈活性差,如果有較大的方案變更驱闷,需要發(fā)版才能解決耻台;如果存在bug,在當前版本修復的難度比較大(iOS的JSPatch方案和Android的Dex修復方案);需要根據不同的平臺寫不同的代碼空另,iOS主要為object_c和swift盆耽,android為Java。

而作為H5為主要開發(fā)模式的Web App的靈活性就比較強扼菠,他利用操作系統(tǒng)中的h5容器作為一個承載摄杂,對外提供一個url鏈接,而該url鏈接對應的內容可以實時在服務端進行修改娇豫,靈活行很強匙姜,避免了Native發(fā)版周期帶來的時間成本。但是h5雖然靈活冯痢,但是他也有自己的硬傷氮昧。每次都需要下載完整的UI數據(html,css浦楣,js)袖肥,弱網用戶體驗較差,流量消耗較大振劳;無法調用系統(tǒng)文件系統(tǒng)椎组,硬件資源等等;

Native App和Web App都有他們的優(yōu)勢和劣勢历恐。我們也不能一棍子拍死說誰好誰劣寸癌。通常的經驗是:對于一些比較穩(wěn)當的業(yè)務,對用戶體驗要求較高的弱贼,我們可以選擇Native開發(fā)蒸苇。而對于一些業(yè)務變更比較快、處在不斷試水的過程吮旅,而且不涉及調用文件系統(tǒng)和硬件調用的業(yè)務我們可以選擇h5開發(fā)溪烤。所以說,在一款app中我們需要同時支持Native代碼和h5代碼。這也是我們標題所說的Native開發(fā)中需要H5容器的必要性檬嘀。

iOS存在的h5容器主要包括UIWebView和WKWebView槽驶,下面我們就分別來說說他們的用法和優(yōu)劣。

二鸳兽、UIWebView的基本用法

2.1掂铐、加載網頁

UIWebView*webView = [[UIWebViewalloc] initWithFrame:self.view.bounds];? ? webView.delegate =self;? ? [self.view addSubview:webView];//網絡地址NSURL*url = [[NSURLalloc] initWithString:@"http://www.taobao.com"];NSURLRequest*request = [NSURLRequestrequestWithURL:url];? ? [webView loadRequest:request];

2.2、UIWebViewDelegate幾個常用的代理方法

//進行加載前的預判斷贸铜,如果返回YES堡纬,則會進入后續(xù)流程(StartLoad,FinishLoad)聂受。如果返回NO蒿秦,這不會進入后續(xù)流程。- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType;//開始加載網頁- (void)webViewDidStartLoad:(UIWebView*)webView;//加載完成- (void)webViewDidFinishLoad:(UIWebView*)webView;//加載失敗- (void)webView:(UIWebView*)webView didFailLoadWithError:(nullableNSError*)error;

2.3蛋济、Native調用JS中的方法

比如我們在加載的HTML文件中有如下js代碼:

functionhello(){? ? alert("你好棍鳖!");}functionhelloWithName(name){? ? alert(name +",你好碗旅!");}

我們可以調用- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;函數進行js調用渡处。

[webView stringByEvaluatingJavaScriptFromString:@"hello()"];[webView stringByEvaluatingJavaScriptFromString:@"helloWithName('jack')"];

js代碼不一定要在js文件中預留,也可以在代碼中通過字符串的形式進行調用祟辟,比如下面:

//自定義js函數NSString*jsString =@"function sayHello(){ \

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? alert('jack11')? \

? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? \

? ? ? ? ? ? ? ? ? ? ? ? ? sayHello()";? ? [_webView stringByEvaluatingJavaScriptFromString:jsString];NSString*jsString =@" var p = document.createElement('p'); \

? ? ? ? ? ? ? ? ? ? ? ? ? ? p.innerText = 'New Line';? ? ? ? ? ? \

? ? ? ? ? ? ? ? ? ? ? ? ? ? document.body.appendChild(p);? ? ? ? \

? ? ";? ? [_webView stringByEvaluatingJavaScriptFromString:jsString];

2.4医瘫、JS中調用Naitve的方法

具體讓js通知native進行方法調用,我們可以讓js產生一個特殊的請求旧困〈挤荩可以讓Native代碼可以攔截到,而且不然用戶察覺吼具。業(yè)界一般的實現方案是在網頁中加載一個隱藏的iframe來實現該功能僚纷。通過將iframe的src指定為一個特殊的URL,實現在- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;方案中進行攔截處理拗盒。對應的js調用代碼如下:

functionloadURL(url){variFrame;? ? ? ? iFrame =document.createElement("iframe");? ? ? ? iFrame.setAttribute("src", url);? ? ? ? iFrame.setAttribute("style","display:none;");? ? ? ? iFrame.setAttribute("height","0px");? ? ? ? iFrame.setAttribute("width","0px");? ? ? ? iFrame.setAttribute("frameborder","0");document.body.appendChild(iFrame);// 發(fā)起請求后這個iFrame就沒用了怖竭,所以把它從dom上移除掉iFrame.parentNode.removeChild(iFrame);? ? ? ? iFrame =null;? ? }

比如我們在js代碼中,調用一下兩個js方法:

functioniOS_alert(){//調用自定義對話框loadURL("alert://abc");? ? }functioncall(){//? js中進行撥打電話處理loadURL("tel://17715022071");? ? }

當你觸發(fā)以上方法的時候陡蝇,就會進入webview的代理方法中進行攔截痊臭。

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType{NSURL* url = [request URL];if([[url scheme] isEqualToString:@"alert"]) {//攔截請求,彈出自定義對話框UIAlertView* alertView = [[UIAlertViewalloc] initWithTitle:@"test"message:[url host] delegate:nilcancelButtonTitle:@"OK"otherButtonTitles:nil];? ? ? ? [alertView show];returnNO;? ? }elseif([[url scheme] isEqualToString:@"tel"]){//攔截撥打電話請求BOOLresult = [[UIApplicationsharedApplication] openURL:url];if(!result) {NSLog(@"您的設備不支持打電話");? ? ? ? }else{NSLog(@"電話打了");? ? ? ? }returnNO;? ? }returnYES;}

這樣我們就可以讓js進行native的調用登夫。

三广匙、WKWebView的基本用法

3.1、加載網頁

WKWebView*webView = [[WKWebViewalloc] initWithFrame:[UIScreenmainScreen].bounds];NSURL*url = [NSURLURLWithString:@"http://www.taobao.com"];NSURLRequest*request = [NSURLRequestrequestWithURL:url];? ? [webView loadRequest:request];? ? [self.view addSubview:webView];

3.2悼嫉、幾個常用的代理方法

/**

*? 根據webView艇潭、navigationAction相關信息決定這次跳轉是否可以繼續(xù)進行,這些信息包含HTTP發(fā)送請求,如頭部包含User-Agent,Accept,refer

*? 在發(fā)送請求之前,決定是否跳轉的代理

*? @param webView

*? @param navigationAction

*? @param decisionHandler

*/- (void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void(^)(WKNavigationActionPolicy))decisionHandler{? ? decisionHandler(WKNavigationActionPolicyAllow);}/**

*? 這個代理方法表示當客戶端收到服務器的響應頭蹋凝,根據response相關信息鲁纠,可以決定這次跳轉是否可以繼續(xù)進行。

*? 在收到響應后鳍寂,決定是否跳轉的代理

*? @param webView

*? @param navigationResponse

*? @param decisionHandler

*/- (void)webView:(WKWebView*)webView decidePolicyForNavigationResponse:(WKNavigationResponse*)navigationResponse decisionHandler:(void(^)(WKNavigationResponsePolicy))decisionHandler{? ? decisionHandler(WKNavigationResponsePolicyAllow);}/**

*? 準備加載頁面改含。等同于UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType

*

*? @param webView

*? @param navigation

*/- (void)webView:(WKWebView*)webView didStartProvisionalNavigation:(null_unspecifiedWKNavigation*)navigation{}/**

*? 這個代理是服務器redirect時調用

*? 接收到服務器跳轉請求的代理

*? @param webView

*? @param navigation

*/- (void)webView:(WKWebView*)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecifiedWKNavigation*)navigation{? ? }- (void)webView:(WKWebView*)webView didFailProvisionalNavigation:(null_unspecifiedWKNavigation*)navigation withError:(NSError*)error{? ? }/**

*? 內容開始加載. 等同于UIWebViewDelegate: - webViewDidStartLoad:

*

*? @param webView

*? @param navigation

*/- (void)webView:(WKWebView*)webView didCommitNavigation:(null_unspecifiedWKNavigation*)navigation{? ? }/**

*? 頁面加載完成。 等同于UIWebViewDelegate: - webViewDidFinishLoad:

*

*? @param webView

*? @param navigation

*/- (void)webView:(WKWebView*)webView didFinishNavigation:(null_unspecifiedWKNavigation*)navigation{? ? }/**

*? 頁面加載失敗迄汛。 等同于UIWebViewDelegate: - webView:didFailLoadWithError:

*

*? @param webView

*? @param navigation

*? @param error? ? ?

*/- (void)webView:(WKWebView*)webView didFailNavigation:(null_unspecifiedWKNavigation*)navigation withError:(NSError*)error{? ? }- (void)webViewWebContentProcessDidTerminate:(WKWebView*)webViewNS_AVAILABLE(10_11,9_0){? ? }/*

我們看看WKUIDelegate的幾個代理方法捍壤,雖然不是必須實現的,但是如果我們的頁面中有調用了js的alert鞍爱、confirm鹃觉、prompt方法,我們應該實現下面這幾個代理方法睹逃,然后在原來這里調用native的彈出窗盗扇,因為使用WKWebView后,HTML中的alert沉填、confirm疗隶、prompt方法調用是不會再彈出窗口了,只是轉化成ios的native回調代理方法

*/#pragma mark - WKUIDelegate- (void)webView:(WKWebView*)webView runJavaScriptAlertPanelWithMessage:(NSString*)message initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void(^)(void))completionHandler{UIAlertController*alertView = [UIAlertControlleralertControllerWithTitle:@"h5Container"message:message preferredStyle:UIAlertControllerStyleAlert];//? ? [alertView addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {//? ? ? ? textField.textColor = [UIColor redColor];//? ? }];[alertView addAction:[UIAlertActionactionWithTitle:@"我很確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction* _Nonnull action) {? ? ? ? completionHandler();? ? }]];? ? [selfpresentViewController:alertView animated:YEScompletion:nil];}

顯然WKWebView的代理方法提供了比UIWebView顆粒度更細的方法翼闹。讓開發(fā)者可以進行更加細致的配置和處理斑鼻。

3.3 、Native調用JS中的方法

WKWebView提供的調用js代碼的函數是:

- (void)evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void(^ __nullable)(__nullableid,NSError* __nullableerror))completionHandler;

比如我們在加載的HTML文件中有如下js代碼:

functionhello(){? ? alert("你好猎荠!");}functionhelloWithName(name){? ? alert(name +"坚弱,你好!");}

我們可以調用如下代碼進行js的調用:

[_wkView evaluateJavaScript:@"hello()"completionHandler:^(iditem,NSError* error) {? ? ? }];? ? [_wkView evaluateJavaScript:@"helloWithName('jack')"completionHandler:^(iditem,NSError*error) {? ? ? }];

同UIWebView一樣法牲,我們也可以通過字符串的形式進行js調用史汗。

NSString*jsString =@"function sayHello(){ \

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? alert('jack11')? \

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? \

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sayHello()";? ? [_wkView evaluateJavaScript:jsString completionHandler:^(iditem,NSError*error) {? ? ? ? ? ? }];? ? ? ? jsString =@" var p = document.createElement('p'); \

? ? p.innerText = 'New Line';? ? ? ? ? ? \

? ? document.body.appendChild(p);? ? ? ? \

? ? ";? ? [_wkView evaluateJavaScript:jsString completionHandler:^(iditem,NSError*error) {? ? ? ? ? ? }];

3.4、JS中調用Naitve的方法

除了和UIWebView加載一個隱藏的ifame之外拒垃,WKWebView自身還提供了一套js調用native的規(guī)范停撞。

我們可以在初始化WKWebView的時候,給他設置一個config參數悼瓮。

//高端配置//創(chuàng)建配置WKWebViewConfiguration*config = [[WKWebViewConfigurationalloc] init];//創(chuàng)建UserContentController(提供javaScript向webView發(fā)送消息的方法)WKUserContentController*userContent = [[WKUserContentControlleralloc] init];//添加消息處理戈毒,注意:self指代的是需要遵守WKScriptMessageHandler協議,結束時需要移除[userContent addScriptMessageHandler:selfname:@"NativeMethod"];//將UserContentController設置到配置文件中config.userContentController = userContent;//高端的自定義配置創(chuàng)建WKWebView_wkView = [[YXWKView alloc] initWithFrame:self.view.bounds configuration:config];NSURL*url = [NSURLURLWithString:@"http://localhost:8080/myDiary/index.html"];NSURLRequest*request = [NSURLRequestrequestWithURL:url];? ? [_wkView loadRequest:request];? ? _wkView.UIDelegate =self;? ? _wkView.navigationDelegate =self;? ? [self.view addSubview:_wkView];

我們在js可以通過NativeMethod這個Handler讓js代碼調用native横堡。

比如在js代碼中埋市,我新增了一個方法

functioninvokeNativeMethod(){window.webkit.messageHandlers.NativeMethod.postMessage("我要調用native的方法");? ? }

觸發(fā)以上方法的時候,會在native以下方法中進行攔截處理命贴。

#pragma mark - WKScriptMessageHandler- (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message{//這里就是使用高端配置道宅,js調用native的處理地方食听。我們可以根據name和body,進行橋協議的處理污茵。NSString*messageName = message.name;if([@"NativeMethod"isEqualToString:messageName]) {idmessageBody = message.body;NSLog(@"%@",messageBody);? ? }}

四樱报、UIWebView和WKWebView的比較和選擇

WKWebView是蘋果在WWDC2014發(fā)布會中發(fā)布IOS8的時候公布WebKit時候使用的新型的H5容器。它與UIWebView相比較泞当,擁有更快的加載速度和性能迹蛤,更低的內存占用。將UIWebViewDelegate和UIWebView重構成了14個類襟士,3個協議盗飒,可以讓開發(fā)者進行更加細致的配置。

但是他有一個最致命的缺陷陋桂,就是WKWebView的請求不能被NSURLProtocol截獲逆趣。而我們團隊開發(fā)的app中對于H5容器最佳的優(yōu)化點主要就在于使用NSURLProtocol技術對于H5進行離線包的處理和H5的圖片和Native的圖片公用一套緩存的技術。因為該問題的存在章喉,目前我們團隊還沒有使用WKWebView代替UIWebVIew汗贫。



本文轉自鏈接:http://www.reibang.com/p/84a6b1ac974a

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末身坐,一起剝皮案震驚了整個濱河市秸脱,隨后出現的幾起案子,更是在濱河造成了極大的恐慌部蛇,老刑警劉巖摊唇,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異涯鲁,居然都是意外死亡巷查,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門抹腿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岛请,“玉大人,你說我怎么就攤上這事警绩〕绨埽” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵肩祥,是天一觀的道長后室。 經常有香客問我,道長混狠,這世上最難降的妖魔是什么岸霹? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮将饺,結果婚禮上贡避,老公的妹妹穿的比我還像新娘痛黎。我一直安慰自己,他們只是感情好刮吧,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布舅逸。 她就那樣靜靜地躺著,像睡著了一般皇筛。 火紅的嫁衣襯著肌膚如雪琉历。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天水醋,我揣著相機與錄音旗笔,去河邊找鬼。 笑死拄踪,一個胖子當著我的面吹牛蝇恶,可吹牛的內容都是我干的。 我是一名探鬼主播惶桐,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼撮弧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了姚糊?” 一聲冷哼從身側響起贿衍,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎救恨,沒想到半個月后贸辈,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡肠槽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年擎淤,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秸仙。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘴拢,死狀恐怖,靈堂內的尸體忽然破棺而出寂纪,到底是詐尸還是另有隱情席吴,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布弊攘,位于F島的核電站抢腐,受9級特大地震影響,放射性物質發(fā)生泄漏襟交。R本人自食惡果不足惜迈倍,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捣域。 院中可真熱鬧啼染,春花似錦宴合、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至斜棚,卻和暖如春阀蒂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弟蚀。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工蚤霞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人义钉。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓昧绣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捶闸。 傳聞我的和親對象是個殘疾皇子夜畴,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容