【问题标题】:ReactNative - Webview not executing injectedJavaScript on AndroidReactNative - Webview 未在 Android 上执行注入的 JavaScript
【发布时间】:2017-07-19 21:58:31
【问题描述】:

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

  1. iOS 模拟器 - 一切顺利
  2. iPhone - 一切顺利
  3. Android 模拟器 - 一切顺利
  4. 物理设备、Sony Xperia Z2、Sony Xperia Z5 Compact 和 LG G4 - 没有

我的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 的标记的一部分,但我真的很想保持简单,只是让它按应有的方式工作.

我正在使用最新版本的 ReactNative (0.41),并且手机运行的是 Android 6+。

【问题讨论】:

  • 我以前遇到过这个问题,我发现Android javascript代码需要在严格模式下编写。其他任何事情都是不可靠的。
  • @wmcbain 那么你会推荐什么?在我注入页面的代码 sn-p 之前添加“use strict”,或者在页面上已经存在的脚本之前添加它(作为html 的一部分)?或两者?我会看看我是否可以测试这两种情况......
  • 很抱歉我说错了。我发现 Android javascript 代码需要正确格式化。例如行尾的; 和函数调用。
  • @wmcbain 嗯...我的代码格式正确,所有行都正确终止。我想我只能将整个脚本批量注入页面并忽略注入的Javascript。
  • 对不起,我帮不上忙,这对我来说也是一个令人沮丧的问题。可能值得看看人们对 Android 网络视图的看法。

标签: android webview react-native


【解决方案1】:

我一直在努力让 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/>}
                    />)
        });
    };
}

【讨论】:

  • 据我所知,webview-bridge 已经是 react-native 核心的一部分
【解决方案2】:

我刚刚发现 Android WebView 似乎将任何 JS 注入为单行,即使它包含换行符。这意味着缺少分号肯定会导致问题,或者在我的情况下,由// 分隔的cmets。对 cme​​ts 使用 /**/ 让我注入的 JavaScript 再次工作。

【讨论】:

  • 它不适用于我的场景。如果您查看 OP,您会发现我只进行了一次函数调用,将 JSON 对象作为参数传递给它。函数调用也以分号终止。
  • 好点;我读得不够仔细。 :-) 另外,我明白了,我在模拟器上遇到了问题,而不仅仅是真实设备。
  • 所以你无法让脚本在模拟器上注入,但它可以在物理设备上运行?正如我在 OP 中所说,我的体验完全相反——模拟器运行良好,但在我测试的所有设备上都失败了。因此,我构建传递给 webview 的 HTML 以模拟脚本注入的解决方案......
  • 这真的很有帮助,我也遇到了同样的问题。不知道单行!
【解决方案3】:

好吧,在将这个问题留待一段时间并找到一个(不是那么优雅的)解决方案之后,我决定分享我最终做了什么:

  1. 我将 HTML 声明为以常量字符串形式传递给 WebView,如下所示:const html = '&lt;html&gt;...&lt;script&gt;...[INJECTEDSCRIPT]&lt;/script&gt;&lt;/html&gt;';
  2. 我检索了 React 组件生命周期的 componentDidMount() 事件中的数据,并使用 this.setState({ data: retrievedData }); 为其设置了一个状态变量
  3. 当然,这会强制组件重新呈现自身,之后我现在可以将数据“传递”给 WebView
  4. 看到我找不到任何优雅或可用的方式来使用 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 />}
      />;
  }
}

【讨论】:

  • 嘿@FarligOpptreden!我一直在努力让 Javascript(预嵌入和注入)也被执行。到目前为止,一个简单更好的解决方案是使用WebViewBridge。之后,一切都按预期进行。这里是包的链接:cnpmjs.org/package/react-native-webview-bridge
  • @MoneroJeanniton 问题可能已经解决,或者您的建议在最新版本的 ReactNative 中效果更好。我面临的问题(并用我的基本解决方案解决)是 18 个多月前。 :)
猜你喜欢
  • 2016-05-15
  • 1970-01-01
  • 1970-01-01
  • 2011-09-17
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多