更新为Bounty
我目前在我的iframe中使用src =“url”来加载对我来说不是最佳的ReactApp,因为这将允许用户右键单击并“在新窗口中打开”。
我正在寻找的最佳解决方案是将我的bundle.js与一个或类似的解决方案一起写入iframe。 src将保持为空白,因此用户无法在新窗口/选项卡中右键单击以打开。
我正在探索如何使用React制作一个可嵌入的小部件,所以我到目前为止从google搜索到了以下内容。但是,我不渲染,并返回以下消息。
错误消息[错误]不变违规:目标容器不是DOM元素。
我使用create-react-app为我的嵌入式应用程序,我只有2个简单的文件。
index.js
import React from 'react';
import ReactDom from 'react-dom';
import App from './App';
ReactDom.render(
<App />, document.getElementById('root')
)
App.js
import React, { Component } from 'react';
class App extends Component {
render() {
return (
<div>This is an embedable widget</div>
)
}
}
export default App;
我创建了一个test.js,它将在远程iframe中使用下面的简单代码调用以生成html,包含js包,以及包含id的div。
这是test.js
//Creates Div
var d = document.createElement("div");
document.body.appendChild(d);
//Creates iFrame
var n = document.createElement("iframe");
n.id = "microcom-frame";
n.style.width = "100%";
n.style.height = "100%";
n.style.background = "transparent";
n.style.position = "relative";
n.style.margin = 0;
n.style.border = "none";
n.style.overflow ="hidden";
n.style.display = "block";
//Append iFrame inside Div
d.appendChild(n);
//Write content to iFrame
n.contentWindow.document.open("text/html", "replace"),
n.contentWindow.document.write("\n <!doctype html>\n <head><script src='http://localhost:3001/static/js/bundle.js' type='text/javascript'></script></head>\n <body><div id='root'></div></body>\n </html>"),
n.contentWindow.document.close();
现在在远程站点上,我在标题中只有以下脚本来调用上面的test.js。
<script>
(function() {
var d = document,
h = d.getElementsByTagName('head')[0],
s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'http://localhost:3001/test.js';
h.appendChild(s);
} )();
</script>
这是......
但是,没有显示。
感谢有人可以带我到下一步。我正在使用create-react-app创建我的反应文件(这是我学习的唯一方法。)
要满足同源策略(防止CORS错误),请设置<iframe>
的srcdoc
属性,而不是尝试write
。
n.srcdoc = "\n <!doctype html>\n <head><script src='http://localhost:3001/static/js/bundle.js' type='text/javascript'></script></head>\n <body><div id='root'></div></body>\n </html>";
作为额外的好处,您可以使用以下命令禁用右键单击上下文菜单:
n.contentWindow.document.addEventListener("contextmenu", function(e){
e.preventDefault();
return false;
}, false);
这作为一种安全措施是完全没用的,但它只是作为一个红鲱鱼。单独打开框架时显示的页面将不包含内容。只有在用户想要复制的iframe页面中没有任何内容时才能执行此操作;它并没有阻止他们这样做,但如果你想复制一些东西,那真的很烦人。
演示:
//Creates Div
var d = document.createElement("div");
document.body.appendChild(d);
//Creates iFrame
var n = document.createElement("iframe");
n.id = "microcom-frame";
n.style.width = "100%";
n.style.height = "100%";
n.style.background = "transparent";
n.style.position = "relative";
n.style.margin = 0;
n.style.border = "none";
n.style.overflow ="hidden";
n.style.display = "block";
//Append iFrame inside Div
d.appendChild(n);
//Write content to iFrame
n.srcdoc = "<!doctype html><html><head></head><body><div id='root'>Example content</div></body></html>";
出于安全目的,嵌入iframe不是一个好习惯,如果使用沙盒iframe可能会更好。
在测试环境中的DOM中读取目标div之前的脚本,只需更改标记的顺序即可。请参阅下文并查看底部的其他问题,当您移动到野外时,您可能会尝试做什么。
将“async defer”添加到您在初始代码中插入iframe的脚本标记中,如下所示:
n.contentWindow.document.write("\n <!doctype html>\n <head><script async defer src='http://localhost:3001/static/js/bundle.js' type='text/javascript'></script></head>\n <body><div id='root'></div></body>\n </html>"),
您可以在您插入的正文的末尾添加脚本标记(或执行展位并将异步延迟放在其上)
尝试使用srcdoc,它工作得更好,因为它甚至没有提供突破框架的选项(它只是重新加载上面示例中的当前位置)
n.srcdoc = "\n <!doctype html>\n <head></head>\n <body><div id='root'></div><script async defer src='http://localhost:3001/static/js/bundle.js' type='text/javascript'></script></body>\n </html>"
然而。
当我在生产中尝试这个时,它没有加载我的整个应用程序。除了构建中的/ static文件夹中的初始js包之外的任何其他内容都不会加载,因为应用程序会相对于小部件位置查找它们。
你可以通过确保js没有块来解决这个问题(创建反应应用程序不应该分割你的代码,除非你使用一个包专门做那个)。您还需要在应用程序外部加载所有css文件和图像,而不是导入它们。
也可以使用其中一种方法将react应用程序部署到子目录中,以便为应用程序的静态资源设置绝对基本URL。
另外,您可能希望在使用它时从react应用程序中的index.js中删除registerServiceWorker()。
如果仍然会导致问题并且您需要通过src加载它,您可以让react应用程序检查它是否不在框架内并自行杀死,或者在这里通过某些方法取消框架时返回How to identify if a webpage is being loaded inside an iframe or directly into the browser window?
您已在index.js中使用root作为目标容器。可能这不是index.html文件中DOM元素的名称。使它一样,也许它会起作用。