iOS8之后一疯,Apple推出了最新的WKWebView柔纵,經(jīng)歷了若干代的發(fā)展之后眼坏,WKWebView日趨完善,在目前的開發(fā)項目當(dāng)中也得到了充分的使用地粪。
為什么使用WKWebView取募?
因為老舊的UIWebView存在嚴(yán)重的性能和內(nèi)存消耗問題,限制了業(yè)務(wù)的自由度蟆技。WKWebView 采用跨進程方案玩敏,Nitro JS 解析器斗忌,高達 60fps 的刷新率,理論上性能和 Safari 比肩旺聚,而且對 H5 的高度支持织阳。
由于WKWebView使用跨進程方案,不會增加app的使用內(nèi)存砰粹,所以保證了比較好的性能和體驗唧躲。如圖所示,進程分布碱璃。
UIWebView和WKWebView的流程區(qū)別
如圖所示弄痹,WKWebView的流程粒度更加細灸撰,不但在請求的時候會詢問WKWebView是否請求數(shù)據(jù)唆垃,還會在返回數(shù)據(jù)之后詢問WKWebView是否加載數(shù)據(jù)。
#請求數(shù)據(jù)的時候詢問
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
#返回數(shù)據(jù)的時候詢問
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
在流程中杨伙,WKWebView返回的錯誤粒度也比UIWebView細爽航,如代碼所示:
#請求數(shù)據(jù)時發(fā)生的error
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
#請求之后加載H5發(fā)生的error
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
深入JavaScript與Native
JavaScript與Native之間的交互一直是Web與Native的重要行為蚓让,在WKWebView中,JavaScript與Native的交互還是很優(yōu)雅的讥珍。
Native調(diào)用JavaScript
#pragma mark - UIWebView
NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
#pragma mark - WKWebView
[wkWebView evaluateJavaScript:@"document.title"
completionHandler:^(id _Nullable ret, NSError * _Nullable error) {
NSString *title = ret;
}];
WKWebView 提供的接口和 UIWebView 命名上較為類似历极,區(qū)別是 WKWebView 的這個接口是異步的,而 UIWebView 是同步接口
JavaScript調(diào)用Native
WKWebView 綁定共享對象衷佃,是通過特定的構(gòu)造方法實現(xiàn)趟卸,參考代碼,通過指定 UserContentController 對象的 ScriptMessageHandler 經(jīng)過 Configuration 參數(shù)構(gòu)造時傳入纲酗。
WKUserContentController *userContent = [[WKUserContentController alloc] init];
[userContent addScriptMessageHandler:id<WKScriptMessageHandler> name:@"MyNative"];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContent;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
通過addScriptMessageHandler:name:指代實現(xiàn)WKScriptMessageHandler協(xié)議的對象衰腌,以及被js調(diào)用的方法名稱新蟆,結(jié)束是需要移除觅赊。
而handler 對象需要實現(xiàn)指定協(xié)議,實現(xiàn)指定的協(xié)議方法琼稻,當(dāng) JS 端通過 window.webkit.messageHandlers 發(fā)送 Native 消息時吮螺,handler 對象的協(xié)議方法被調(diào)用,通過協(xié)議方法的相關(guān)參數(shù)傳值帕翻。
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
WKWebView的痛點
Cookie問題
這個坑自iOS 8之后一直被Apple公司做出各種調(diào)整甚至到了iOS 10和iOS 11版本在Cookie上依然做出了比較大的調(diào)整鸠补,在傳統(tǒng)的UIWebView中,Cookie是通過NSHTTPCookieStorage統(tǒng)一管理嘀掸,服務(wù)器返回時寫入紫岩,發(fā)起請求時服務(wù),Web和Native能共享Cookie睬塌。在WKWebView中泉蝌,Cookie的寫入不是實時的歇万,而且請求時也不會實時讀取Cookie,這導(dǎo)致了app會丟失cookie勋陪,無法做到session和Native同步贪磺。
跨域問題
跨域問題,HTTPS 對 HTTPS诅愚、HTTP 對 HTTP 跨域默認是能載入的寒锚,但如果是 HTTP 想載入 HTTPS 跨域鏈接,因為安全考慮违孝,WKWebView 會被攔截刹前,這問題在引入跨域 HTTPS 的頁面也做 HTTPS。
關(guān)于WKWebView在iOS11上的新特性
Cookie的管理
iOS11上雌桑,WKWebView 新增了 Cookie 管理 API WKHTTPCookieStore腮郊,通過該接口可以設(shè)置、刪除和查詢 WKWebView cookie筹燕,甚至可以監(jiān)聽 cookie store 的變化轧飞。(但是對于UIWebView和WKWebView混合使用的業(yè)務(wù),需要對WKHTTPCookieStore和NSHTTPCookieStorage之間做一些同步操作撒踪。)
加載本地資源
由于WKWebView的網(wǎng)絡(luò)請求是在非主進程中發(fā)起过咬,所以NSURLProtocol無法攔截請求,在iOS11中制妄,提供了專門的接口加載本地資源掸绞。
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id)urlSchemeTask
但是,依然有坑耕捞,WKWebView不允許攔截 Scheme 為 “http”衔掸、“https”、“ftp”俺抽、“file” 敞映,我想這一目的是為了限制開發(fā)人員只能下載屬于自己的資源而做出的限制,也是出于一種安全的考慮磷斧。
Filter Unwanted
Filter unwanted contentWWDC 2015上振愿,WebKit 團隊介紹了 Safari 上新增的 Content Blocker 特性,可以實現(xiàn)阻止頁面內(nèi)容加載或隱藏頁面內(nèi)容等功能弛饭。在WWDC 2017上冕末,WebKit 團隊將這一特性移植到了 WKWebView。