React Native-在浏览器中打开链接


72

嗨,我正在使用react native的web视图显示一些html,我希望每当用户单击该html中的链接时,它将使用该链接打开用户的浏览器。

那可能吗?

编辑1:

我最终使用了这个软件包:npmjs.com/package/react-native-communications,它打开了浏览器。URL更改时,我调用浏览器在onNavigationStateChange上打开。

现在的问题是,尽管我已移至浏览器,但WebView仍继续处理请求,如何停止请求?


4
嗨,马丹,您最终找到了解决方案吗?我正在处理相同的问题,如果有解决方案,将很有兴趣。我知道在一个UIWebView,我们可以与代理人实现做..
Bocaxica

Answers:


118

这是一个完整的工作解决方案:

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中打开它。

iOS模拟器


10
这对我有用,但是我建议添加event.navigationType === 'click'if条件,仅在浏览器中捕获点击事件...
LaurenzGlück17年

1
仅当我以其他方式进行引用时,这才对我有用-ref="webview"然后this.refs.webview.stopLoading()
Urska '17

@Urska目前可以使用,但已显式弃用了这种方式。参见facebook.github.io/react/docs/…。同样,两种方式都应该相同,因此您可能会遇到其他问题。
damusnet

1
@LaurenzGlück这将是真正有用的,但看不到属性navigationType上的事件,只是urlloadingcanGoForwardcanGoBacktarget,和title。这有点草率,但是canGoBack如果您要处理WebView中的所有链接并且从不让用户在WebView内部导航,则可以使用。
ckeeney

2
在下面的答案中使用onShouldStartLoadWithRequest。android 6与此有关
不幸的

33

使用“ 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
}}

太棒了,这确实修复了许多错误。例如阻止在原始Webview上进行点击,或者有时目标网站在原始Webview上显示一些叠加层,并且会打开外部浏览器。
幸运的

/^[data:text, about:blank]/会匹配很多东西,因为[]是字符类。您可能是/^(?:data:text|about:blank)/故意的。看到这个答案
t.888


15

这是我的解决方案。由于它不是通用的,因此您可以根据需要改进注入的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
Kshitij '17

1
它像一种魅力。但是条件data !== "undefined"应该在if块中添加。
efkan

工作完美!谢谢。
AndyH

你如何使用这个?当我单击链接时,显示MyWebView?
A.com

谢谢,很好。但是data!== undefined应该是字符串“ undefined”,而不是类型undefined。
Dror Bar

9

我找到了一种方法,不过这是仅适用于iOS的解决方案!

这是分步说明:

  1. 'RCTLinkingIOS'库链接到您的项目。我使用了CocoaPods。
  2. 在您的WebView中添加“ onShouldStartLoadWithRequest”。例如

    <WebView source={{ html: content }} onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest} />

  3. 添加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
         });
    }
    
  4. 确保在您的JavaScript源中也导入链接:

    import { AppRegistry, View, WebView, Linking } from 'react-native';

这应该够了吧。

有关更多信息,请参见react-native文档:https : //facebook.github.io/react-native/docs/linking.html


当嵌入了任何外部服务(例如Youtube)时,此方法不起作用。加载后它将自动播放,并带您离开应用程序,然后返回时,它将带您回到Youtube并引起循环。
伊达直人(Naoto Ida)

正如上面已经提到的,onShouldStartLoadWithRequest只存在于iOS的facebook.github.io/react-native/docs/...

onShouldStartLoadWithRequest使用RN 6.14.6与移动浏览器chrome一起使用。
雷米

1

可以使用内置的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)}
/>


1

对于使用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


0

您将要阅读以下内容:https : //facebook.github.io/react-native/docs/linkingios.html

有可能,只需检查上面的链接,您应该一切都很好!

其他连结:

https://github.com/ivanph/react-native-webintent

https://www.npmjs.com/package/react-native-browser


看起来不错,但是我的第一个问题是我不知道如何捕捉到一个事件,该事件说用户单击了链接。有这样的事件吗?我需要使用onNavigationStateChange事件并在那里检查吗?
Matan Kadosh '16

Well I'm assuming that you're wanting to link <Text> to open up a certain link? What you can do is have a <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
James111

no, you got me all wrong. i have a webview displaying html. the html might have links in it. i want that whenever the user clicks on a link he will be moved to the browser
Matan Kadosh

1
I ended up using this package : npmjs.com/package/react-native-communications which opens the browser
Matan Kadosh

我可以在react-native。(react-native-modal)的弹出窗口中打开链接吗?如果用户单击该URL,它将重定向到应用程序外的另一个窗口。因此,我需要在模式窗口中显示打开的URL。有什么建议吗?
sejn

0

如果stopLoading()onNavigationStateChangeAndroid上无法正常使用,

this.webview.stopLoading();
setTimeOut( () => {
  Linking.openURL(event.url)
}, 1000);

运行良好。


0

仅在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
}}

0
  1. If you are attempting to open a redirect url and you get an error from an android intent such as "err_unknown_url_scheme".

  2. Check what it is trying to open because it may have detected android traffic and attempted to open in the corresponding app.

  3. 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://'),
          );
         }
   }
}}


0
<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.

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.