1、监控与重写url跳转。
2、自定义Loading样式不够强大。
3、RN与Web通信的Bridge。
4、一些特殊应用:tel://,mailto://,微信的,甚至于一些自定义协议。
5、将来做自己的WebAPI。
参考链接:
UIWebView-bridge:GItHub链接;
W KWebView:GitHub链接;
上述两个示例,一个可以实现RN与Web的通信,另一个无法通信却可以实现对加载进度的监控,方便我们实现自己的loading.
导入方式我们选择WKWebView的方式导入(项目中我已经导入完毕);
然后在导入的源代码里加入我们要实现的Bridge功能即可。
/*
*Allows custom handling of any webview requests by a JS handler. Return true
*or false from this method to continue loading the request.
*@platform ios
*/
onShouldStartLoadWithRequest: PropTypes.func,
这个方法是webView的一个属性,可以绑定一个函数,例如:
<WKWebView source={{uri:this.state.src}}
ref = {'webviewref'}
startInLoadingState={true}
renderLoading={()=><Text>正在加载页面...</Text>}
style={styles.webViewStyle}
onLoad = {()=>TestMBProgressManager.textExample("加载中.....")}
onShouldStartLoadWithRequest = {(e)=>{
console.log(e);
return true;
}}
injectedJavaScript = {injectScript}
onBridgeMessage = { this.onBridgeMessage.bind(this) }
/>
在这个函数里我们就可以对当前加载的URL进行筛选和判断。
输出的日志示例如下:
{ target: 9,
canGoBack: false,
lockIdentifier: 1189458900,
loading: false,
title: '百度一下',
canGoForward: false,
navigationType: -1,
url: 'wkwvb://message1473669712719' }
在获取到URL之后,返回false就是不让加载当前URL。
若是想跳转新的页面,只需重新设置soruce并reload即可。
在这里提供了多种方式去自定义loading。
(1)在react里自定义视图,设置样式。
示例如下:
//WebView的属性之一,返回一个视图,在加载的时候呈现。
renderLoading={()=><Text>正在加载页面...</Text>}
(2) 通过native实现。
在这里我创建了一个MBPrograssHUD的管理类,可以实现自定义文字、图片等的 加载图。在开始加载的时候,使他显示,加载完毕后,使其消失。
但不建议使用这种方法去实现。
我们用一个简单的打招呼的过程来理解Bridge的使用(这仅仅是一个示例,更复杂的逻辑,根据需求由我们的工程师自己去实现吧。)
首先在建立WebView的时候,我们为当前页面注入如下js代码,由Web向RN打招呼:(reactive中实现)
const injectScript = `
(function(){
if (WebViewBridge) {
WebViewBridge.onMessage = function(message){
if (message === "hello from react-native") {
WebViewBridge.send("got the message inside webview");
}
};
WebViewBridge.send("hello from webview");
}
}());
`;
在native的RCTWebView.m中,我实现了以下方法:
//since there is no easy way to load the static lib resource in ios,
//we are loading the script from this method.
- (NSString*)webViewBridgeScript{
return NSStringMultiline((function (window) {
'use strict';
//Make sure that if WebViewBridge already in scope we don't override it.
if (window.WebViewBridge) {
return;
}
var RNWBSchema = 'wkwvb';
var sendQueue = [];
var receiveQueue = [];
var doc = window.document;
var customEvent = doc.createEvent('Event');
function callFunc(func, message) {
if ('function' === typeof func) {
func(message);
}
}
function signalNative() {
window.location = RNWBSchema + '://message' + new Date().getTime();
}
//I made the private function ugly signiture so user doesn't called them accidently.
//if you do, then I have nothing to say. :(
var WebViewBridge = {
//this function will be called by native side to push a new message
//to webview.
__push__: function (message) {
receiveQueue.push(message);
//reason I need this setTmeout is to return this function as fast as
//possible to release the native side thread.
setTimeout(function () {
var message = receiveQueue.pop();
callFunc(WebViewBridge.onMessage, message);
}, 15); //this magic number is just a random small value. I don't like 0.
},
__fetch__: function () {
//since our sendQueue array only contains string, and our connection to native
//can only accept string, we need to convert array of strings into single string.
var messages = JSON.stringify(sendQueue);
//we make sure that sendQueue is resets
sendQueue = [];
//return the messages back to native side.
return messages;
},
//make sure message is string. because only string can be sent to native,
//if you don't pass it as string, onError function will be called.
send: function (message) {
alert(message);
if ('string' !== typeof message) {
callFunc(WebViewBridge.onError, "message is type '" + typeof message + "', and it needs to be string");
return;
}
//we queue the messages to make sure that native can collects all of them in one shot.
sendQueue.push(message);
//signal the objective-c that there is a message in the queue
signalNative();
},
onMessage: null,
onError: null
};
window.WebViewBridge = WebViewBridge;
//dispatch event
customEvent.initEvent('WebViewBridge', true, true);
doc.dispatchEvent(customEvent);
}(window));
);
}
这是一段js代码,在每次加载完毕后,都为当前应用注入,旨在建立web与RN的通信通道。
在RN中,我们监听Web消息的方法如下:
onBridgeMessage(message) {
// var webview = this.refs.webview.getDOMNode();
// const { webviewref } = this.refs;
const webview = this.refs['webviewref']
;
switch (message) {
case "hello from webview":
webview.sendToBridge("hello from react-native");
console.log('我们打招呼给WebView');
break;
case "got the message inside webview":
console.log("we have got a message from webview!yeah!");
break;
}
}
//绑定到WebView的属性上面
onBridgeMessage = { this.onBridgeMessage.bind(this) }
每当web有消息发送过来的时候,这个方法都会被触发,然后我们就可以在RN里面根据我们自己的需求去处理相应的消息。
这里通过sendToBridge的方法,由RN向web发送消息。
然后又由注入的js代码中的WebViewBridge.onMessage这个函数接受消息,并做处理。(即我们最开始注入的js代码)。
这样,就实现了Bridge的通信。
在native代码中,有这样一个方法:
- (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
在这个方法中:
NSURLRequest *request = navigationAction.request;
NSURL* url = request.URL;
NSString* scheme = url.scheme;
这样就可以根据不同的scheme对不同的协议进行处理了。包括上述bridge,在这里也运用了scheme去实现的,部分代码:
if (isJSNavigation) {
decisionHandler(WKNavigationActionPolicyCancel);
}
else if (navigationAction.targetFrame && ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"])) {
decisionHandler(WKNavigationActionPolicyAllow);
}
else {
if([scheme isEqualToString:@"wkwvb"]){
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
if (![scheme isEqualToString:@"about"]) {
[[UIApplication sharedApplication] openURL:url];
}
decisionHandler(WKNavigationActionPolicyAllow);
}
在这里wkwvb就是RN与web互相通信时,我自定义的协议,如果有其他的协议,都可以在这个方法中进行处理。具体的逻辑,就根据需求由工程师去实现了。
具体逻辑,在将来,根据需求由工程师去实现。
2024 - 快车库 - 我的知识库 重庆启连科技有限公司 渝ICP备16002641号-10
企客连连 表单助手 企服开发 榜单123