嗨,我正在使用react native的web视图显示一些html,我希望每当用户单击该html中的链接时,它将使用该链接打开用户的浏览器。
那可能吗?
编辑1:
我最终使用了这个软件包:npmjs.com/package/react-native-communications,它打开了浏览器。URL更改时,我调用浏览器在onNavigationStateChange上打开。
现在的问题是,尽管我已移至浏览器,但WebView仍继续处理请求,如何停止请求?
嗨,我正在使用react native的web视图显示一些html,我希望每当用户单击该html中的链接时,它将使用该链接打开用户的浏览器。
那可能吗?
我最终使用了这个软件包:npmjs.com/package/react-native-communications,它打开了浏览器。URL更改时,我调用浏览器在onNavigationStateChange上打开。
现在的问题是,尽管我已移至浏览器,但WebView仍继续处理请求,如何停止请求?
Answers:
这是一个完整的工作解决方案:
import React, { Component } from 'react';
import { WebView, Linking } from 'react-native';
export default class WebViewThatOpensLinksInNavigator extends Component {
render() {
const uri = 'http://stackoverflow.com/questions/35531679/react-native-open-links-in-browser';
return (
<WebView
ref={(ref) => { this.webview = ref; }}
source={{ uri }}
onNavigationStateChange={(event) => {
if (event.url !== uri) {
this.webview.stopLoading();
Linking.openURL(event.url);
}
}}
/>
);
}
}
它使用一个简单的WebView,拦截任何URL更改,如果该URL与原始URL不同,则停止加载,防止页面更改,然后在OS Navigator中打开它。
event.navigationType === 'click'
if条件,仅在浏览器中捕获点击事件...
ref="webview"
然后this.refs.webview.stopLoading()
。
navigationType
上的事件,只是url
,loading
,canGoForward
,canGoBack
,target
,和title
。这有点草率,但是canGoBack
如果您要处理WebView中的所有链接并且从不让用户在WebView内部导航,则可以使用。
使用“ stopLoading()”的一个大问题是,在Android上,它禁用了对该源页面上任何其他链接的进一步点击。
WebView组件正在从核心RN中分离出来,并交到社区的手中。如果您改用该版本(https://github.com/react-native-community/react-native-webview),则可以在iOS和Android上使用“ onShouldStartLoadWithRequest”道具,这使它更加优雅。
在Damien Varron真正有用的答案的基础上,下面是一个示例,说明如何利用该道具来避免跨平台工作的stopLoading:
onShouldStartLoadWithRequest={event => {
if (event.url !== uri) {
Linking.openURL(event.url)
return false
}
return true
}}
如果您的源是HTML而不是URI,则可以按照以下方法进行操作:
onShouldStartLoadWithRequest={event => {
if (event.url.slice(0,4) === 'http') {
Linking.openURL(event.url)
return false
}
return true
}}
正如basbase所指出的,您也可以用这种方式(我已经添加了about:blank部分)。我计划进行更多的反复试验,以查看哪种效果最好。
onShouldStartLoadWithRequest={event => {
if (!/^[data:text, about:blank]/.test(event.url)) {
Linking.openURL(event.url)
return false
}
return true
}}
Linking.openURL(url).catch(err => console.error('An error occurred', err));
这是我的解决方案。由于它不是通用的,因此您可以根据需要改进注入的javascript
import {
View,
WebView,
Linking,
} from 'react-native';
const injectScript = `
(function () {
window.onclick = function(e) {
e.preventDefault();
window.postMessage(e.target.href);
e.stopPropagation()
}
}());
`;
class MyWebView extends React.Component {
onMessage({ nativeEvent }) {
const data = nativeEvent.data;
if (data !== undefined && data !== null) {
Linking.openURL(data);
}
}
render() {
return (
<WebView
source={{ html: this.props.html }}
injectedJavaScript={injectScript}
onMessage={this.onMessage}
/>
)
}
}
Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined.
您是如何克服这个错误的?我一直在用您的解决方案来解决这个问题。在响应本机存储库中也有一个问题-github.com/facebook/react-native/issues/10865。
data !== "undefined"
应该在if
块中添加。
我找到了一种方法,不过这是仅适用于iOS的解决方案!
这是分步说明:
'RCTLinkingIOS'
库链接到您的项目。我使用了CocoaPods。在您的WebView中添加“ onShouldStartLoadWithRequest”。例如
<WebView source={{ html: content }} onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest} />
添加this.onShouldStartLoadWithRequest
功能。根据您是否要遵循WebView中的链接,返回TRUE或FALSE。这基本上与以本机代码(Swift或Objective-C)实现UIWebViewDelegate实现时等效。
onShouldStartLoadWithRequest = (event) => {
Linking.canOpenURL(event.url).then(supported => {
if (supported) {
Linking.openURL(event.url);
} else {
console.log('Don\'t know how to open URI: ' + event.url);
}
return false
});
}
确保在您的JavaScript源中也导入链接:
import { AppRegistry, View, WebView, Linking } from 'react-native';
这应该够了吧。
有关更多信息,请参见react-native文档:https : //facebook.github.io/react-native/docs/linking.html
可以使用内置的Linking类和react-native-webview-bridge以跨平台的方式执行此操作。
const generateLink = (text, url) => {
if (url) {
return `<a href='#' onclick='WebViewBridge.send("${url}"); return false;'>${text}</a>`;
} else {
return text;
}
};
const generateHtml = () => `<!DOCTYPE html><html><body>
${generateLink('Link text', 'http://example.com')}
</body></html>`;
使用如下所示的WebViewBridge
组件:
<WebViewBridge
javaScriptEnabled
renderLoading={() => <ActivityIndicator animating size="large" />}
source={{ html: generateHtml() }}
onBridgeMessage={url => Linking.openURL(url)}
/>
除了出色的答案https://stackoverflow.com/a/40382325/10236907:使用时source={{html: '...'}}
,您还可以使用以下方法检查外部网址更改:
if (!/^data:text/.test(event.url)) {
对于使用expo的人的解决方案:
import * as WebBrowser from 'expo-web-browser';
import { WebView } from 'react-native-webview';
...
render() {
return (
<WebView
source={{
uri: uri
}}
ref={ (ref) => { this.webview = ref; } }
onNavigationStateChange={ (event) => {
if (event.url !== uri) {
this.webView.stopLoading();
WebBrowser.openBrowserAsync(event.url);
}
}}
/>
);
}
WebBrowser
通过以下方式安装: expo install expo-web-browser
您将要阅读以下内容:https : //facebook.github.io/react-native/docs/linkingios.html
有可能,只需检查上面的链接,您应该一切都很好!
其他连结:
<TouchableHighlight onPress={this.function}> <Text>link goes here...</Text></TouchableHighlight>
. This.function would be a function that would make use of the above npm packages. @MatanKadosh
仅在iOS上使用本地HTML文件时,此解决方案存在很多问题。例如,当我将Instagram嵌入到我的Web视图中时,它会自动将其打开。为了解决这个问题,我添加了(仅适用于iOS):
onShouldStartLoadWithRequest={event => {
const condition = isIos ? event.navigationType == 'click' : true
if (event.url.slice(0, 4) === 'http' && condition) {
Linking.openURL(event.url)
return false
}
return true
}}
If you are attempting to open a redirect url and you get an error from an android intent such as "err_unknown_url_scheme".
Check what it is trying to open because it may have detected android traffic and attempted to open in the corresponding app.
If you wish for it to open in the webview anyway and you get an intent back from a redirect, intercept it in onNavigationStateChange and rewrite it:
onNavigationStateChange={event => {
if (event.url !== uri) {
if (event.url.match('intent://')) {
SetSearchClick(
event.url.replace('intent://', 'https://'),
);
}
}
}}
<WebView
source={{uri: this.state.src}}
ref={(webView) => {
this.webView.ref = webView;
}}
onNavigationStateChange={(navState) => {
this.webView.canGoBack = navState.canGoBack;
if (!navState.url.includes('yourdomain.com')) {
Linking.openURL(navState.url);
return false;
}
}}
onShouldStartLoadWithRequest={(event) => {
if (!event.url.includes('yourdomain.com')) {
Linking.openURL(event.url);
return false;
}
return true;
}}
style={{flex: 1}}
startInLoadingState={true}
renderLoading={() => {
return (
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
}}
/>
I struggled with this one, my use case was to open external links in a separate browser. This solution worked for me.