ReactNative - Android上的Webview无法执行注入的JavaScript

9
我在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+。

我以前遇到过这个问题,我发现安卓的Javascript代码需要使用严格模式编写。否则,它会不可靠。 - wmcbain
@wmcbain 那你会推荐什么呢?是在我注入到页面的代码片段之前添加"use strict",还是在已经存在于页面(作为html的一部分)的脚本之前添加它?还是两者都加?我会尝试测试这两种情况... - FarligOpptreden
很抱歉我说错了。我发现Android的JavaScript代码需要格式良好,例如在行末和函数调用处需要加上; - wmcbain
@wmcbain 嗯...我的代码格式良好,所有行都正确终止。我想我只能将整个脚本块注入页面并忽略injectedJavascript。 - FarligOpptreden
抱歉我无法提供帮助,对我来说这也是一个令人沮丧的问题。也许值得看看人们对于Android Web视图的看法。 - wmcbain
@wmcbain 没关系,感谢您尝试帮助。看来我离答案还很远,所以我不得不手动将脚本注入到Webview所提供的HTML中。我会保留这个问题未解决,直到有人提供一些见解,说明为什么它不能正常工作或如何使脚本注入。 - FarligOpptreden
3个回答

8

我刚刚发现Android WebView注入的任何JS都似乎被作为单行注入,即使它包括换行也是如此。这意味着缺失的分号肯定会引起问题,或者就像我的情况一样,由//分隔的注释。使用/**/作为注释可以使我的插入JS再次正常工作。


它对我的情况没有起作用。如果您查看原始帖子,您会发现我只进行了一次函数调用,并将JSON对象作为参数传递给它。函数调用也以分号终止。 - FarligOpptreden
好的观点,我没有仔细阅读。 :-) 另外,我发现我在模拟器上遇到了问题,而不仅是真实设备。 - David Alan Hjelle
所以你无法在模拟器上注入脚本,但在实体设备上却可以?正如我在原帖中所述,我的经历恰好相反 - 模拟器完美运行,但在我测试的所有设备上都失败了。因此,我的解决方案是构建传递给Webview的HTML,以模拟脚本注入... - FarligOpptreden
这真是非常有帮助,我也遇到了同样的问题。不知道还可以使用单行代码! - karma

4

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

  1. 我声明了传递给WebView的HTML为常量字符串,如下所示:const html = '<html>...<script>...[INJECTEDSCRIPT]</script></html>';
  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。之后,一切都按预期工作了。这里是包的链接:https://cnpmjs.org/package/react-native-webview-bridge - Monero Jeanniton
@MoneroJeanniton 可能问题已经解决,或者你提出的建议在最新版本的 ReactNative 中效果更好。我遇到这个问题并用自己简陋的方法解决已经超过18个月了。 :) - FarligOpptreden

0

我一直在努力让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 核心的一部分。 - karma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接