【问题标题】:React Native - open links in browserReact Native - 在浏览器中打开链接
【发布时间】:2016-06-02 13:59:31
【问题描述】:

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

这可能吗?

编辑 1:

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

现在的问题是,虽然我已经移动到浏览器,但 WebView 仍然继续处理请求,我该如何停止请求?

【问题讨论】:

  • 嗨,Matan,您最后找到解决方案了吗?我正在处理同样的问题,如果有解决方案会很感兴趣。我知道在 UIWebView 中我们可以通过委托实现来做到这一点..

标签: react-native


【解决方案1】:

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

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

【讨论】:

  • 这对我有用,但我建议在 if 条件中添加 event.navigationType === 'click' 以仅捕获浏览器中的点击事件...
  • 只有当我以不同的方式引用时才对我有用 - ref="webview" 然后 this.refs.webview.stopLoading()。
  • @Urska 虽然这暂时有效,但这种方式已被明确弃用。见facebook.github.io/react/docs/…。此外,这两种方式应该是一样的,所以你可能会遇到另一个问题。
  • @LaurenzGlück 这真的很有帮助,但在活动中看不到navigationType 的属性,只有url、loading、canGoForward、canGoBack、target和title。这有点草率,但如果您想处理 WebView 中的所有链接并且永远不要让用户在 WebView 内导航,canGoBack 可以工作。
  • 在下面的答案中使用 onShouldStartLoadWithRequest。 android 6 有这个问题
【解决方案2】:

使用“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)/。见this answer。
【解决方案3】:
Linking.openURL(url).catch(err => console.error('An error occurred', err));

https://facebook.github.io/react-native/docs/linking.html

【讨论】:

  • 它有效。有什么方法可以像现代应用程序一样在应用程序中打开浏览器。我正在使用世博会
【解决方案4】:

这是我的解决方案。由于它不是通用的,您可以根据您的要求改进注入的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. 你是如何克服这个错误的?我一直在用你的解决方案来解决这个问题。在 react native repo 中也存在一个问题 - github.com/facebook/react-native/issues/10865。
  • 它就像一个魅力。但是data !== "undefined" 的条件应该添加到if 块中。
  • 完美运行!谢谢。
  • 你如何使用这个?当我点击一个链接时,我会显示 MyWebView?
  • 谢谢,非常好。但 data !== undefined 应该是字符串“undefined”,而不是 undefined 类型。
【解决方案5】:

我找到了一种方法来做到这一点,但它是一个仅限 iOS 的解决方案!

以下是分步说明:

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

    &lt;WebView source={{ html: content }} onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest} /&gt;

  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,从而导致循环。
  • 上面已经提到,onShouldStartLoadWithRequest 只存在于 iOS facebook.github.io/react-native/docs/…
  • onShouldStartLoadWithRequest 与移动浏览器 chrome 一起使用,使用 RN 6.14.6。
【解决方案6】:
<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>
        );
      }}
    />

我在这个问题上遇到了困难,我的用例是在单独的浏览器中打开外部链接。这个解决方案对我有用。

【讨论】:

    【解决方案7】:

    @Damusnet 只是偶尔为我工作。我查看了它,这是我使用 react-native-webview 11.4.3 对我有用的更新代码。

    import React from 'react';
    import { WebView, Linking } from 'react-native';
    
    const WebviewScreen = () => {
      const uri = 'http://stackoverflow.com/questions/35531679/react-native-open-links-in-browser';
    
      return (
        <WebView
        source={{ uri }}
        onShouldStartLoadWithRequest={(request) => {
          if (request.url !== uri) {
            Linking.openURL(request.url);
            return false;
          }
    
          return true;
        }}
      />    
      )
    }
    

    【讨论】:

      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

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

          【讨论】:

            【解决方案11】:

            除了出色的答案https://stackoverflow.com/a/40382325/10236907: 使用 source={{html: '...'}} 时,您可以使用以下命令检查外部 url 更改: if (!/^data:text/.test(event.url)) {

            【讨论】:

              【解决方案12】:
              1. 如果您尝试打开重定向 url 并且收到来自 android intent 的错误,例如“err_unknown_url_scheme”。

              2. 检查它尝试打开的内容,因为它可能已经检测到 android 流量并尝试在相应的应用中打开。

              3. 如果你希望它在 webview 中打开并且你从重定向中得到一个意图,在 onNavigationStateChange 中拦截它并重写它:

              onNavigationStateChange={event => {
                if (event.url !== uri) {
                    if (event.url.match('intent://')) {
                        SetSearchClick(
                            event.url.replace('intent://', 'https://'),
                        );
                       }
                 }
              }}
              
              

              【讨论】:

                【解决方案13】:

                你会想读这个: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 事件并在那里检查吗?
                • 好吧,我假设您要链接 以打开某个链接?你可以做的是有一个&lt;TouchableHighlight onPress={this.function}&gt; &lt;Text&gt;link goes here...&lt;/Text&gt;&lt;/TouchableHighlight&gt; 。 this.function 将是一个使用上述 npm 包的函数。 @MatanKadosh
                • 不,你完全错了。我有一个显示 html 的 webview。 html 中可能有链接。我希望每当用户点击链接时,他都会被移动到浏览器
                • 我最终使用了这个包:npmjs.com/package/react-native-communications 打开浏览器
                • 我可以在 react-native 的弹出窗口中打开链接吗?(react-native-modal)。如果用户单击该 url,它将重定向到应用程序之外的另一个窗口。所以我需要在模态窗口中显示打开的 url。有什么建议吗?
                【解决方案14】:

                如果 Android 上 onNavigationStateChange 中的 stopLoading() 不起作用,

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

                效果很好。

                【讨论】:

                  猜你喜欢
                  • 2021-01-12
                  • 2018-12-17
                  • 2012-02-21
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多