前言
在9012的最后一篇写到了在rn中安卓的webview的通信原理,而作为0202年的第一篇,继续讨论上年rn中webview通信剩下的部分。
背景:
对于webview,了解过的人都知道在ios端会存在两种类型的webview(UIWebview和WKWebview),而他们之间的区别主要如下:
- 时间上的差异:UIwebview是在ios2之后才有,Wkwebview是在ios8之后才有;
- UIwebview占用的内存较大,而WKwebview侧是内存占有不多,因此性能会比前者好;
- WKwebview拥有高达60FPS滚动刷新率(滚动时每秒可达到60次渲染,一般最多64次)及内置手势;
- WKwebview比UIwebview拥有更多H5的特性;
- WKwebview提供了加载网页的进度属性;
- WKwebview在app和web通信方面比UIwebview要好;
- WKWebView没有做缓存处理,所以对网页需要缓存的加载性能要求没那么高的还是可以考虑UIWebView.
RN中IOS的webview结构:
大致流程:
在ios中,应用层会设置rn中webview的某个属性,而weview会通过webview.iso.js进行处理,如果该属性是原生底层直接拥有的,那么就直接往下传递,如果该属性不是底层所拥有的,则需要通过在webview.ios.js层处理成底层组件所拥有的属性,然后再往下传递下去,由RCTWebview或者RCTWKWebview进行处理。
例如:
source属性:webview层设置了source -----> webview.ios.js往下传递 ------> RCTWebviewManager.m能接受到该属性,然后继续往下传递 -----> RCTWebview进行处理该属性;
onLoadStart属性:webview层设置了onLoadStart ----> webview.ios.js对该属性进行处理,包装成_onLoadingStart,然后传给RCTWebviewManager ----> RCTWebviewManager.m能接受到该属性,然后继续往下传递 -----> RCTWebview进行处理该属性;
大概的流程图:
UIWebview的相关知识:
ios和js的交互
- stringEvaluatingJavascriptFromString:该方法时可以让ios层执行js代码;
语法形式:
- (void)injectJavaScript:(NSString *)script
{
[_webView stringByEvaluatingJavaScriptFromString:script];
}
当js层传递js字符串给ios的时候,ios就会用stringEvaluatingJavascriptFromString这个方法执行这个script。
- shouldStartLoadWithRequest: 该方法是用来对webview中的url进行拦截,然后判断是否打开这个url链接,需要返回一个boolean值。当webview中存在一个url链接,就会触发这个方法,然后对url进行判断,如果是返回boolean,则可以打开url的链接,否则不行。
生命周期
对于uiwebview来说,一般有三个生命周期他们分别是:
- shouldStartLoadWithRequest:页面在准备加载资源;
- webViewDidFinishLoad:页面资源完成加载完毕;
- didFailLoadWithError:资源加载失败;
rn中UIWebview和js之间的通信机制:
在webview之中通信最为重要,通过postMessage向rn层传递信息,rn层只需要message就行,相反也行。该实现的逻辑是在资源加载完成的时候执行的,就是在webviewDidFinishLoad
内嵌页面向rn传递信息和接受信息:
window.postMessage(data);
document.addEventListener('message', (event) => {});
rn层向内嵌页面传递信息:
<Webview
onMessage={event => {}}
ref={(_ref) => {this.webview = _ref}}
...
/>
this.webview.postMessage(data)
细心的朋友此处应该可以发现出这里的postMessage和平时我们调用的不太一样:
一般情况下:window.postMessage(data, origin);
内嵌在webview之中:window.postMessage(data);
原因是因为在uiwebview之中重新定义了window.postMessage,原来的postmessage已经被赋予到originMessage之中,所以如果在内嵌页面中出现了postmessage(data),放在浏览器会报错,放在rn的webview就会没事。
当页面资源加载完成之后,就会触发webviewDidFinishLoad这个生命周期,在这个生命周期内,会做几件事情:
其中重新定义postmessage的js代码:
大概的意思就是将内嵌页面调用postmessage的信息存在messagequeue之中(以字符串形式放进去),然后通过定义window.location的值来传递信息给原生成,其中window.location的格式是:
'%@://%@?' + encodeURIComponent(messageQueue.shift());" ,当中的两个%@会被RCTJSNavigationScheme和kPostMessageHost代替从而形成新的url地址(不是常用的http协议的地址,是指定的特殊协议)
例如:'react-js-navigation://postMessage?' + encodeURIComponent('{"A":1}') ===> "react-js-navigation://postMessage?%7B%22A%22%3A1%7D";由于内嵌页面没有window,所以只能用document来监听webview传来的信息。
postmessage的信息如何传递给原生层:每当webview之中出现url的请求,都会触发shouldStartLoadWithRequest,此时就会由
大概就是判断url的host是否为kPostMessageHost,如果是则认为是postmessage传来的,然后执行_onMessage方法,而这个方法事由上层js定义的:
webview如何将信息传递给内嵌页面:
大概就是通过document.dispatchEvent发起一个事件,然后在内嵌页面之中由document进行监听;
下面就是整一块uiwebview的通信流程:
所以如果要js层调用原生层能力,可以通过协议请求,然后在原生对该协议进行拦截处理,从而判断是否调用原生能力。
以上就是对uiwebview的一个比较浅的概述,下一篇会继续介绍一下rn中wkwebview的通信原理。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。