WKWebView终究要入坑
栏目: JavaScript · 发布时间: 5年前
内容简介:对于大多开发者来说,苹果WWDC2018大会关注比较多的是iOS 12、新的ARKit、新的CoreML,其实还有一个更改在session上没有具体提及,但对于开发者来说影响挺大,如下图:,既然苹果爸爸要放弃UIWebView,开发者只能在适配的时候共勉之。有些项目适配起来相对比较简单,因为不涉及复杂的交互逻辑,只需要替换底层的Web容器,改下调用的API就行了,但是在适配一个较为复杂的跨端交互项目时就比较gg了,因为WKWebView不使用JavaScriptCore相关的API,而是使用自己的一套机制进
对于大多开发者来说,苹果WWDC2018大会关注比较多的是iOS 12、新的ARKit、新的CoreML,其实还有一个更改在session上没有具体提及,但对于开发者来说影响挺大,如下图:
虽然只是加个小小的“Deprecated”标签,但可以看出苹果已经放弃对UIWebView这个组件的维护,希望开发者全量地切换到WKWebView这个组件上面。虽然WKWebView已经在iOS8已经推出,相比UIWebView拥有更低的内存消耗和更快的JavaScript引擎,但是我们的苹果大佬给我们开发者埋了太多坑了,按道理来说WKWebView应该更快更6,可是开发者用起来苦不堪言,所以市面上还是很多开发者还是没有放弃UIWebView,因为它用起来比较稳定,想了解具体有哪些大坑可以看腾讯bugly写的这篇文章 《WKWebView 那些坑》,既然苹果爸爸要放弃UIWebView,开发者只能在适配的时候共勉之。
对于项目的影响
有些项目适配起来相对比较简单,因为不涉及复杂的交互逻辑,只需要替换底层的Web容器,改下调用的API就行了,但是在适配一个较为复杂的跨端交互项目时就比较gg了,因为WKWebView不使用JavaScriptCore相关的API,而是使用自己的一套机制进行JS与原生桥接,而且JS调用方式与JavaScriptCore完全不同,如果原生只是简单地替换下容器和改下交互的API,那我们的h5同学又得加班加点了,上层JS API在新的容器是无法调用。
进化吧,XDMicroJSBridge
为了h5同学完美过渡,无需改动任何桥接代码,只需关注自己的业务迭代,我们的桥接框架需要来一次大升级,代码“WK”。因为不能使用JavaScriptCore,所以XDMicroJSBridge底层桥接框架得重新设计,这次我们使用WKWebView的WKScriptMessageHandler进行桥接层的设计。
New API ?
我们先看下新的框架接口层的设计:
看起来API层是不是没什么改变,除了要保证我们的h5能完美过渡,我们也要保证我们的原生端接入新框架的时候也能完美过渡,这样原生这边也不用改桥接代码。
容器配置
唯一的改变就是容器这一块的API交互,以前是使用方传进容器,现在是我们里面提供配置好的容器给外部,降低原生同学使用的复杂度,确实WKWebView这个容器相比UIWebView这个容器更加灵活,各种各样的配置项。
- (WKWebView *)getBridgeWebView { WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; // 设置偏好设置 config.preferences = [[WKPreferences alloc] init]; // 默认为0 config.preferences.minimumFontSize = 10; // 默认认为YES config.preferences.javaScriptEnabled = YES; // 在iOS上默认为NO,表示不能自动通过窗口打开 config.preferences.javaScriptCanOpenWindowsAutomatically = NO; config.processPool = [[WKProcessPool alloc] init]; config.userContentController = [[WKUserContentController alloc] init]; //解决self 循环引用问题 XDWKWeakScriptMessageDelegate *weakSelf = [[XDWKWeakScriptMessageDelegate alloc] initWithDelegate:self]; [config.userContentController addScriptMessageHandler:weakSelf name:@"XDWKJB"]; WKUserScript *injectScript = [[WKUserScript alloc] initWithSource:injectJS injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; [config.userContentController addUserScript:injectScript]; _webview = [[WKWebView alloc] initWithFrame:CGRectNull configuration:config]; return _webview; } 复制代码
这里有个注意点就是WKWebView在注册JS的消息回调这一块需要弄个额外的对象进行回调注册,如果直接用self会造成循环引用导致内存泄露,经过实验写成weakSelf也不管用。
WKWebView有个人性化的设计就是能够原生化的进行JS预加载,而且能够指定加载时机和位置,相比之下,以前在UIWebView进行JS预加载一点都不pure。
桥接层设计
WKWebView提供WKScriptMessageHandler进行JS层的消息捕获,例如注册一个名为XDWKJB的桥接对象,JS层那边通过 window.webkit.messageHandlers.XDWKJB.postMessage(e)
这个方法将消息e发送到原生这边,原生这边在WKScriptMessageHandler的回调方法 - (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message
进行消息的捕获和解析。
这次的设计需要引入一定JS代码,因为WKWebView提供的JS交互只支持简单的数据传输,当存在callback的时候,当前的交互并不能将js的callback完美转化。所以需要设计一个js的callback管理器用于保存从js的callback,在js层先赋予callback一个id并映射起来,因为id可以设计为简单的数据格式,如字符串,所以原生这边很容易捕获和解析,当原生回调数据时通过callback管理器获取对应id的callback,执行对应callback的js代码就可以实现桥接回调,代码实例如下:
var XDMCBridge = {};var xd_jscallback_center={_callbackbuf:{},addCallback:function(a,c){\"function\"==typeof c&&(this._callbackbuf[a]=c)},fireCallback:function(a,c){if(\"string\"==typeof a){var f=this._callbackbuf[a];\"function\"==typeof f&&(void 0===c?f():f(c))}}}; 复制代码
h5完美过渡方案
因为上个版本的XDMicroJSBridge提供的JS API是类微信web API风格,我们这次继续沿用这种代码风格,为了适配这种风格,所以这次的框架需要提供一些模板JS代码来辅助JS方法注册和注入,模板实例如下:
%@.%@=function(){var a=arguments.length,e={methodName:\"%@\"},l=Array.from(arguments);a>0&&(\"function\"==typeof l[a-1]?(e.callbackId=\"%@\",e.params=a-1>0?l.slice(0,a-1):[]):e.params=l),null!=e.callbackId&&xd_jscallback_center.addCallback(e.callbackId,l[a-1]),window.webkit.messageHandlers.XDWKJB.postMessage(e)}; 复制代码
%@是占位符,用于替换注册的JS接口的命名空间名和方法名
最终效果
XDMicroJSBridge拓展出XDMicroJSBridge_WK,用于适配WKWebView,同样支持命名空间,原生专注原生代码,web专注JavaScript
初始化Bridge
#import "XDMicroJSBridge_WK.h" @property (nonatomic, strong) WKWebView *webView; @property (nonatomic, strong) XDMicroJSBridge_WK *bridge; @property (nonatomic, copy) XDMCJSBCallback callback; self.bridge = [[XDMicroJSBridge_WK alloc] init]; 复制代码
注册JS方法
跟上个版本注册方式一模一样
__weak typeof(self) weakself = self; [_bridge registerAction:@"camerapicker" handler:^(NSArray *params, XDMCJSBCallback callback) { dispatch_async(dispatch_get_main_queue(), ^{ //if your javaScript method has callback, you should register this call like this. if (callback) { weakself.callback = callback; } UIImagePickerController *cameraVC = [[UIImagePickerController alloc] init]; cameraVC.delegate = weakself; cameraVC.sourceType = UIImagePickerControllerSourceTypeCamera; [weakself presentViewController:cameraVC animated:YES completion:nil]; }); }]; 复制代码
h5调用原生注册的JS方法
也跟上个版本调用一模一样,h5同学会不会很开心:blush:,不用改代码
<script> function clickcamera() { XDMCBridge.camerapicker(function (response) { var photos = response['photos']; var insert = document.getElementById('insert'); for(var i = 0; i < photos.length; i++) { var img = new Image(100,100); img.src = photos[i]; insert.appendChild(img); } }); } </script> 复制代码
以上所述就是小编给大家介绍的《WKWebView终究要入坑》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。