两种方式方式 (重点讲一下第二种方式)
1.使用url解析
用法介绍
最简单的方式,在需要与原生交互的地方,使用
1 | window.location('scheme://host/path?query') |
然后在原生的地方截取到请求,进行scheme判断,走原生逻辑。UIWebView代理
1 | shouldStartLoadWithRequest |
WKWebView代理
1 | navigationDelegate.decidePolicyForNavigationAction |
原生调用js:
1 | UIWebView:stringByEvaluatingJavaScriptFromString WKWebView:evaluateJavaScript:(NSString *)javaScriptString completionHandler: |
三方库
- WebViewJavascriptBridge 原生注入,使用人多,长期维护
- tcoulter/jockeyjs 原生h5注入都可以,安卓和iOS都有,维护少
- …
这种方式的好处
- UIWebView和WKWebView可以共用一套逻辑,如果需要支持iOS8.0以下的版本,可以两个共存做优化
缺点
- 需要一个新的subframe去做location
- 需要url的解析规则,参数太多太复杂,会加大解析和调试难度
2.使用javascriptcore进行js解析和提供执行环境
UIWebView 与 JSCore
JSCore介绍
- 1.获取JSContext:
在webviewdidfinish里面
1 | JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; |
- 2.注入一个方法
1 | context[@"test1"] = ^(){}; |
- 3.注入一个带参数和回调的方法
1 | 原生 |
- 4.注入一个带参数和回调为一个字符加上原生block的方法
1 | 原生: |
- 5.注入一个对象:
1 | 写一个协议 |
- 6.注入时机
上面注入已经完成,交互,回调等都可以组装完成,写容器的时候我们经常会遇到一个问题,原生的代码注入时机太慢,导致javascript运行的时候,原生的方法和类出现undefine,这里需要修改注入时机,不再使用webviewdidfinish。
大概写一个NSObject的category,复写方法:
1 | - (void)webView:(id)unused didCreateJavaScriptContext:(JSContext*)ctx forFrame:(id<TSWebFrame>)frame |
在这里把JSContext付值给webview,然后加一个delegate回调告诉webview,context已经获取到了,最后直接用这个就好了。
具体看:JSCore的注入时机
UIWebView交互层大概完成。
WKWebView
WKWebView不支持JSCore,所以我们不得不换一种思路
1 | WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];// 创建UserContentController(提供JavaScript向webView发送消息的方法) |
注入了一个NativeMethod的message接受
实现协议WKScriptMessageHandler
1 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ |
本身是不支持回调的
假如我们要支持回调怎么做:
用一层封装,把所有需要的东西都扔进去,比如xxx.send(‘xxx’,function()),这些回调,在里面和原生设置一个id,表示回调的函数id,最后在原生事件完成后,再调用这个ID的函数。原理和截取url的一样
最后: 实现一套整合wkwebview uiwebview和jscore的方案