WKWebView实践与详解

iOS8之后,Apple推出了最新的WKWebView,经历了若干代的发展之后,WKWebView日趋完善,在目前的开发项目当中也得到了充分的使用。

为什么使用WKWebView?

因为老旧的UIWebView存在严重的性能和内存消耗问题,限制了业务的自由度。WKWebView 采用跨进程方案,Nitro JS 解析器,高达 60fps 的刷新率,理论上性能和 Safari 比肩,而且对 H5 的高度支持。

由于WKWebView使用跨进程方案,不会增加app的使用内存,所以保证了比较好的性能和体验。如图所示,进程分布。


屏幕快照 2018-07-02 下午3.21.10.png

UIWebView和WKWebView的流程区别

屏幕快照 2018-07-02 下午3.22.33.png

如图所示,WKWebView的流程粒度更加细,不但在请求的时候会询问WKWebView是否请求数据,还会在返回数据之后询问WKWebView是否加载数据。

#请求数据的时候询问
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
#返回数据的时候询问
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;

在流程中,WKWebView返回的错误粒度也比UIWebView细,如代码所示:

#请求数据时发生的error
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
#请求之后加载H5发生的error
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

深入JavaScript与Native

JavaScript与Native之间的交互一直是Web与Native的重要行为,在WKWebView中,JavaScript与Native的交互还是很优雅的。

Native调用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 命名上较为类似,区别是 WKWebView 的这个接口是异步的,而 UIWebView 是同步接口

JavaScript调用Native

WKWebView 绑定共享对象,是通过特定的构造方法实现,参考代码,通过指定 UserContentController 对象的 ScriptMessageHandler 经过 Configuration 参数构造时传入。

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:指代实现WKScriptMessageHandler协议的对象,以及被js调用的方法名称,结束是需要移除。

而handler 对象需要实现指定协议,实现指定的协议方法,当 JS 端通过  window.webkit.messageHandlers 发送 Native 消息时,handler 对象的协议方法被调用,通过协议方法的相关参数传值。
#pragma mark - WKScriptMessageHandler
  -  (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message

WKWebView的痛点

Cookie问题

这个坑自iOS 8之后一直被Apple公司做出各种调整甚至到了iOS 10和iOS 11版本在Cookie上依然做出了比较大的调整,在传统的UIWebView中,Cookie是通过NSHTTPCookieStorage统一管理,服务器返回时写入,发起请求时服务,Web和Native能共享Cookie。在WKWebView中,Cookie的写入不是实时的,而且请求时也不会实时读取Cookie,这导致了app会丢失cookie,无法做到session和Native同步。

跨域问题

跨域问题,HTTPS 对 HTTPS、HTTP 对 HTTP 跨域默认是能载入的,但如果是 HTTP 想载入 HTTPS 跨域链接,因为安全考虑,WKWebView 会被拦截,这问题在引入跨域 HTTPS 的页面也做 HTTPS。

关于WKWebView在iOS11上的新特性

Cookie的管理

iOS11上,WKWebView 新增了 Cookie 管理 API WKHTTPCookieStore,通过该接口可以设置、删除和查询 WKWebView cookie,甚至可以监听 cookie store 的变化。(但是对于UIWebView和WKWebView混合使用的业务,需要对WKHTTPCookieStore和NSHTTPCookieStorage之间做一些同步操作。)

加载本地资源

由于WKWebView的网络请求是在非主进程中发起,所以NSURLProtocol无法拦截请求,在iOS11中,提供了专门的接口加载本地资源。

- (void)webView:(WKWebView *)webView startURLSchemeTask:(id)urlSchemeTask

但是,依然有坑,WKWebView不允许拦截 Scheme 为 “http”、“https”、“ftp”、“file” ,我想这一目的是为了限制开发人员只能下载属于自己的资源而做出的限制,也是出于一种安全的考虑。

Filter Unwanted

Filter unwanted contentWWDC 2015上,WebKit 团队介绍了 Safari 上新增的 Content Blocker 特性,可以实现阻止页面内容加载或隐藏页面内容等功能。在WWDC 2017上,WebKit 团队将这一特性移植到了 WKWebView。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,065评论 25 708
  • *此一朝血色山河 倾十年看尽沉舸 千古的江山画卷 抹不掉历史铁戈 旧年里 赤金戎甲 转如今 青衣谋划 躲不过宿命深...
    拂尘为君_阅读 626评论 10 9
  • 人情的牵绊,你道只是负担,却不知它们也将孤独填满。 在这冷清而寂寞的深秋午后,我迫不及待的找寻着温暖之物,柜子、抽...
    停停停云阅读 281评论 0 0
  • 昨晚下了阵冬雨加雪,今早一早又飘落了些。恰好今日早晨要上第一节课,我如往常一样开车出门。起初只是有雾,打开闪光照常...
    Daisy1982阅读 225评论 1 1