背景
关于代码编辑器,如果我不存储数据,那么如何做到可以直接运行传入的代码呢?这就涉及到跨域通信了。下面我们一起来看看吧。
有哪些方式可以跨域呢?
在 JavaScript 中,跨域通信是指在不同源(如不同域名、协议、端口)之间传递数据。以下是常用的跨域通信 API:
1. postMessage
postMessage
是最常见的跨域通信方式。它允许不同窗口(例如,父页面与 iframe
,不同浏览器标签页之间,甚至不同的浏览器窗口之间)安全地进行数据传递。
特点:
- 通过
postMessage
,你可以在不同源(如不同域名、协议、端口)之间进行通信。 - 使用
postMessage
时,需要确保双方都能够验证消息的来源(通过event.origin
)和消息内容。
示例:
-
发送方(父页面):
javascriptconst iframe = document.getElementById("myIframe"); iframe.contentWindow.postMessage("Hello from parent!", "http://example.com");
-
接收方(iframe 页面):
javascriptwindow.addEventListener("message", function(event) { if (event.origin === "http://example.com") { console.log("Received message:", event.data); } });
2. CORS (Cross-Origin Resource Sharing)
CORS 是一种通过 HTTP 头部来允许跨域请求的机制。它允许服务器指定哪些外部域名的请求是被允许的,从而实现跨域请求。
特点:
- 需要服务器支持 CORS。
- 主要用于处理跨域的 AJAX 请求,尤其是当前端使用
fetch
或XMLHttpRequest
时。 - 浏览器通过在 HTTP 请求中自动添加特殊的
Origin
头部来确定请求的源,服务器通过响应中的Access-Control-Allow-Origin
等头部来决定是否允许该请求。
示例:
-
客户端(发起跨域请求):
javascriptfetch('http://example.com/data', { method: 'GET', headers: { 'Content-Type': 'application/json' }, mode: 'cors' }) .then(response => response.json()) .then(data => console.log(data));
-
服务器端(Node.js 示例,允许跨域请求):
javascriptconst express = require('express'); const app = express(); app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); next(); }); app.get('/data', (req, res) => { res.json({ message: 'Hello from server!' }); }); app.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
3. WebSockets
WebSocket 是一种全双工通信协议,允许浏览器和服务器之间进行实时、持久的连接,支持跨域通信。
特点:
- 可以通过 WebSocket 建立持久的连接,跨越域和协议的限制。
- 适用于实时数据更新、聊天应用、在线游戏等需要长时间连接的场景。
- 客户端和服务器通过
ws://
或wss://
协议建立 WebSocket 连接。
示例:
-
客户端 :
javascriptconst socket = new WebSocket('ws://example.com/socket'); socket.onopen = function(event) { console.log('Connection opened:', event); socket.send('Hello from client!'); }; socket.onmessage = function(event) { console.log('Message from server:', event.data); }; socket.onerror = function(error) { console.log('WebSocket Error:', error); };
4. window.name
window.name
是一个可以用作跨域通信的技巧。通过将数据存储在 window.name
属性中并跳转到目标页面(可能是一个不同的域),你可以在不同的页面之间共享数据。
特点:
window.name
是跨域的,可以在跳转页面后保持数据。- 数据在页面跳转后依然可用,因此常用于跨域数据传递。
示例:
-
客户端(页面 A):
javascriptwindow.name = JSON.stringify({ message: 'Hello from A' }); window.location.href = 'http://example.com/targetPage';
-
接收方(页面 B):
javascriptconst data = JSON.parse(window.name); console.log(data.message); // 输出: Hello from A
5. document.domain
在同一顶级域名下,不同子域之间可以使用 document.domain
来进行跨域通信。通过设置 document.domain
为相同的值,子域之间可以共享资源和通信。
特点:
- 仅适用于相同顶级域名下的不同子域之间的跨域通信。
- 使用
document.domain
可以让两个不同子域之间的 JavaScript 进行交互。
示例:
-
父页面(
subdomain1.example.com
):javascriptdocument.domain = 'example.com';
-
子页面(
subdomain2.example.com
):javascriptdocument.domain = 'example.com';
-
这样,
subdomain1.example.com
和subdomain2.example.com
之间的 JavaScript 就可以互相访问了。
6. Server-Sent Events (SSE)
SSE 是一种单向的通信方式,服务器可以通过它推送信息到客户端。适用于服务器向多个客户端推送实时事件的场景。
特点:
- 服务器向客户端推送数据,支持跨域。
- 与 WebSocket 不同,SSE 是单向通信,客户端无法向服务器发送消息。
示例:
-
客户端:
javascriptconst eventSource = new EventSource('http://example.com/events'); eventSource.onmessage = function(event) { console.log('New event:', event.data); }; eventSource.onerror = function(error) { console.error('EventSource failed:', error); };
-
服务器端:
javascriptconst express = require('express'); const app = express(); app.get('/events', (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); res.flushHeaders(); setInterval(() => { res.write('data: Hello from server\n\n'); }, 1000); }); app.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
总结
常见的 JavaScript 跨域通信 API 有:
postMessage
:适用于不同窗口、iframe 或跨源通信。- CORS:允许跨域的 AJAX 请求。
- WebSockets:用于实时双向通信,跨域支持。
window.name
:跨域数据传递的技巧。document.domain
:适用于同一顶级域名下的不同子域之间的跨域通信。- Server-Sent Events (SSE):服务器向客户端单向推送数据,支持跨域。
最终选择了postMessage API
根据代码编辑器,可以看到,我们采用的是postMessage API,如下所示:
js
const iframe = document.getElementById('child');
const message = [
{
type: 'html',
content: '<h1 id="title">Hello,eveningwater!</h1>'
},
{
type: 'css',
content: 'h1{color:#2396ef;}'
},
{
type: 'js',
content: 'document.getElementById("title").onclick = () => {alert("Hello,eveningwater!");}'
}
];
iframe.addEventListener("load", () => {
iframe.contentWindow.postMessage(message, "*");
});
我们只需要在接收端监听一下message事件即可,如下所示:
js
window.addEventListener("message", function (event) {
if (Array.isArray(event.data)) {
// 拿到数据赋值给编辑器即可
setEditorContent(event.data);
}
});
感谢阅读本文。