JSNI 脚本失败,但在 Chrome Devtools 控制台中 运行 时脚本成功

JSNI script fails, but script succeeds when ran in Chrome Devtools console

我正在使用测试数据通过 JSNI 使用名为 cytoscape.js 的外部 java 脚本库。当我 运行 脚本如何在我的 java class 中通过 JSNI 时,它无法生成图形。但是,当我通过 Chrome Devtools 控制台 运行 它时,它可以正常工作。

所有值似乎都正确地传递给了 cytoscape.js 库。目前,JSNI 代码未能通过控制台代码能够通过的 Javascript 库执行的测试。

这是我使用的 JSNI 代码:

public static native void cytoscape() /*-{
        var cy = $wnd.cy = $wnd.cytoscape({container: $wnd.document.getElementById('cy'),
            elements: $wnd.glyElements, 
            style: [ { selector: 'node', style: { 'background-color': '#666', 'label': 'data(id)' } }, 
                { selector: 'edge', style: { 'width': 3, 'line-color': '#ccc', 'target-arrow-color': '#ccc', 'target-arrow-shape': 'triangle' } } ],
            layout: { name: 'grid', rows: 1 } });
    }-*/;

“$wnd”。用于获取正确的范围。

控制台代码如下:

var cy = window.cy = cytoscape({ 
            container: document.getElementById('cy'),
            elements: glyElements, 
            style: [ { selector: 'node', style: { 'background-color': '#666', 'label': 'data(id)' } }, { selector: 'edge', style: { 'width': 3, 'line-color': '#ccc', 'target-arrow-color': '#ccc', 'target-arrow-shape': 'triangle' } } ], 
            layout: { name: 'grid', rows: 1 } 
        });

我在这两种情况下使用的元素都存储在一个 .js 文件中,并且我确保它们在这两种情况下都可以访问。就像我提到的,Devtools 调试显示正确的值被传递到 cytoscape.js 库。

JSNI 代码无法通过 cytoscape.js 库中的 return 语句:

var plainObject = function plainObject(obj) {
    return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
  };

如果我注释掉“&& obj.constructor === Object;”在 cytoscape.js 的第 145 行,代码 运行 使用我的 JSNI 脚本是正确的。这让我相信 cytoscape 对象是通过 JSNI 与控制台创建的不同。尽管如此,开发工具将控制台和 JSNI 方法列为提供相同的对象。我如何更新我的 JSNI 代码以使其符合此检查?

理想情况下,在这种情况下应该可以使用 JSNI。我不确定为什么通过 JSNI 传递值会导致此 if 语句在 运行 通过控制台传递 if 语句时失败。

问题在于 Object 与 Object 并不完全相同,检查 instanceof 几乎肯定比 cytoscape 的作者认为的要多得多 - JS 不是很棒吗?

cytoscape.js 项目的 instanceof Object 测试不仅仅是 "is this thing a plain Object rather than an instance of some other type" 的测试,而是实际测试 "is this thing an instance of the Object class in my window instance, and not from any other iframe/window"。

GWT 的默认链接器会评估 iframe 中的代码,以防止意外泄漏全局变量和混淆加载到同一页面的 JS,或者让同一页面上的其他 JS 覆盖或以其他方式混淆 GWT 的代码。

除了尝试将 cytoscape 的代码更正为不那么死​​板和不灵活的代码外,还有一些方法可以解决此问题。

  • 从外部页面创建对象的实例:为此,您必须直接创建对象,而不是使用 {...} 语法,它默认为您碰巧正在执行的任何 window在。像这样的东西:
       public static native void cytoscape() /*-{
           var obj = new $wnd.Object();
           obj.container = $wnd.document.getElementById('cy');//can also be simply $doc.getElementById('cy')
           obj.elements = $wnd.glyElements;
           // Note that you may have to repeat this for each of these nested objects, depending
           // on how picky the library is being on otherwise identically structured code...
           obj.style = [ { selector: 'node', style: { 'background-color': '#666', 'label': 'data(id)' } }, 
                   { selector: 'edge', style: { 'width': 3, 'line-color': '#ccc', 'target-arrow-color': '#ccc',
   'target-arrow-shape': 'triangle' } } ];
           obj.layout = { name: 'grid', rows: 1 };
           var cy = $wnd.cy = $wnd.cytoscape(obj);
       }-*/; 
  • 将 cytoscape.js 加载到执行 GWT 代码的同一个 iframe 中。请注意,它实际上可能无法在那里工作,这会导致其他问题,但您可以尝试使用 ScriptInjector 将脚本内容插入到您的 GWT 应用程序,而不是直接在您的 .html 页面中引用它。