首页 文章

ReactNative - Webview不在Android上执行injectJavaScript

提问于
浏览
6

我无法在ReactNative上获取Webview以在物理Android设备上执行注入的JavaScript . 在过去的两天里,我尽可能地搜索网络,但仍未找到解决方案 . 测试结果如下:

  • iOS模拟器 - 一切都很好

  • iPhone - 一切都很好

  • Android模拟器 - 一切都很好

  • 物理设备,索尼Xperia Z2,索尼Xperia Z5 Compact和LG G4 - NOTHING

我的Webview定义如下:

<WebView
  style={styles.webView}
  source={{
    html: html,
    baseUrl: 'web/'
  }}
  injectedJavaScript={'render(' + JSON.stringify(this.state.data) + ');'}
  javaScriptEnabledAndroid={true}
  scrollEnabled={false}
  bounces={false}
  renderLoading={() => <LoadingIndicator />}
/>

我也尝试过指定 javaScriptEnabled ,但无济于事 . 我还尝试使用较小的脚本来对页面上的元素进行着色或使用 window.postMessage 将消息发回给应用程序,但没有任何反应 . 我需要将数据注入HTML,这将根据提供的数据为我呈现图形 . 我最后的办法是手动构建HTML,并附加数据作为提供给Webview的标记的一部分,但我真的想保持简单,只需将其转换为 work the way it should .

我正在使用最新版本的ReactNative(0.41),手机正在运行Android 6 .

3 回答

  • 6

    我刚刚发现Android WebView似乎将任何JS注入 a single line ,即使它包含换行符 . 这意味着丢失的分号肯定会导致问题,或者在我的情况下,由 // 分隔的注释 . 使用 /**/ 进行评论后,我的注入JavaScript再次运行 .

  • 0

    好吧,在将这个问题保持开放一段时间并找到(不那么优雅)问题的解决方案后,我决定分享我最终做的事情:

    • 我声明HTML沿着这些行以常量字符串传递给WebView: const html = '<html>...<script>...[INJECTEDSCRIPT]</script></html>';

    • 我检索了React组件生命周期的 componentDidMount() 事件中的数据,并使用 this.setState({ data: retrievedData }); 为其设置了一个状态变量

    • 这当然迫使组件重新渲染自己,之后我现在可以将数据提供给WebView "pass"

    • 由于我找不到使用 injectedJavaScript 属性以我想要的方式工作的任何优雅或可用的方式(如问题中所述),我使用序列化数据替换HTML常量中的 [INJECTEDSCRIPT] 值 .

    我知道,这不是最优雅的解决方案,但它是我能够在众多设备和仿真器配置中可靠工作的唯一解决方案 . 样本,为简洁起见编辑,如下:

    const html = `
    <!DOCTYPE html>
    <html>
      <head>...</head>
      <body>...</body>
      <script>
        var render = function (data) {
          ...
        };
        [INJECTEDSCRIPT]
      </script>
    </html>`;
    
    export class GraphComponent extends Component {
      constructor(props) {
        super(props);
        this.state = {};
      }
      componentDidMount = () => {
        SERVICE.getData().done((data) => {
          this.setState({ data: data });
        });
      }
      render = () => {
        if (!this.state.data)
          return <LoadingIndicator />;
        let serializedData = JSON.stringify(this.state.data);
        return
          <WebView
            style={styles.webView}
            source={{
              html: html.replace('[INJECTEDSCRIPT]', 'render(' + serializedData + ');'),
              baseUrl: 'web/'
            }}
            scrollEnabled={false}
            bounces={false}
            renderLoading={() => <LoadingIndicator />}
          />;
      }
    }
    
  • 4

    我一直在努力让Javascript(预嵌入和注入)也被执行 . 到目前为止,一个简单而更好的解决方案是使用WebViewBridge . 之后,一切都按预期工作 . 这里有一个包的链接:cnpmjs.org/package/react-native-webview-bridge .

    这是一个演示:

    import React, {
        Component
    } from 'react';
    
     import PropTypes from 'prop-types';
    
     import {
         Platform,
         WebView,
         ActivityIndicator,
    } from 'react-native'; 
    
    import WevViewBridge from 'react-native-webview-bridge';
    
    // TODO: Keep in mind that you should ALWAYS edit
    //       two separate file and keep them matched.
    const IOS_WEB_PAGE_SOURCE = './webPage/wordBody.html';
    const ANDROID_WEB_PAGE_SOURCE = 'file:///android_asset/webPage/wordBody.html';
    
    export default class WordBody extends Component {
    
        static propTypes = {
            data: PropTypes.object.isRequired,
        }
    
        _injectedScript = `
            (function(){
                let data = ${JSON.stringify(this.props.data)};
                refresh(data);
            })();
        `;
    
        render() {
            /*
             * We are using Platform.select here for two reasons:
             * 0. Everythig work fine for IOS using `WebView` from RN
             * 1. Android doesn't load web ressource with `require`
             * 2. On Android, `WebView` from react-native doesn't
             *    execute JavaScript so we use `WebViewBridge`. 
             */
            return Platform.select({
                ios: (<WebView
                        style = {{backgroundColor: 'rgba(0, 0, 0, 0)', flex:1,}}
                        scalesPageToFit
                        source = {require(IOS_WEB_PAGE_SOURCE)}
                        javaScriptEnabled={true}
                        originWhitelist={['*']}
                        injectedJavaScript = {this._injectedScript}
                        renderLoading={() => <ActivityIndicator/>}
                    />),
                android: (<WevViewBridge
                            style = {{backgroundColor: 'rgba(0, 0, 0, 0)', flex:1,}}
                            scalesPageToFit
                            source = {{uri:ANDROID_WEB_PAGE_SOURCE}}
                            javaScriptEnabled={true}
                            // originWhitelist={['*']}
                            injectedJavaScript = {this._injectedScript}
                            renderLoading={() => <ActivityIndicator/>}
                        />)
            });
        };
    }
    

相关问题