Web通信中的跨文档通信
跨文档通信(cross-document messaging)与消息通道通信(channel messaging)简介
跨文档通信
Web中由于安全与隐私的原因不允许执行跨站脚本,以免不同域的文档上下文互相影响。而跨文档通信则是在防止XSS的同时允许不同域之间文档通信的方法。
以下场景均属于跨文档通信
- Window与其创建的WebWorker的通信
- Window与其包含的跨域iFrame的通信
- Window与其打开的同域popup Window的通信
可以看到参与通信的双方有:Window、Popup Window、iFrame与Web Worker。下面是简单的代码示例:
// Window <=> Popup window(same origin)
let popup = window.open('popup.html') // main window
popup.postMessage('hello popup window~')
window.addEventListener("message", e => window.source.postMessage('hi main window', e.origin)); // popup window
// Window <=> iFrame
document.querySelector('iframe').contentWindow.postMessage('hello iframe~') // main window
window.addEventListener('message', e => window.parent.postMessage('hi main window~')) // iframe
// Window <=> Web Worker
(new Worker('worker.js')).postMessage('hello worker~') // main window
self.onmessage = e => self.postMessage('hi main window~') // dedicated worker
安全性:通过设置targetOrigin参数来指定通信的对方。默认为'/',会限定通信对方为同域目标。
消息通道通信
如果要使用独立的代码块在不同的浏览器上下文中进行通信,可以使用消息通道进行通信(channel messaging)。
通过MessageChannel与MessagePort来实现。
- MessageChannel为消息通信类,具有两个只读的MessagePort:port1与port2,为消息通道所使用的两个端口
- 将一个端口作为本地端口,将另一个端口传递给远程窗口使用
- 消息会以DOM事件的方式传递,不会中断或阻塞事件循环中的task执行
// 1.创建一个连接
const channel = new MessageChannel()
// 2.主窗口发送消息与端口
window.postMessage('message', '*', [channel.port1])
// 3.使用另一个端口监听消息
channel.port2.onmessage = e => {
console.log('get data: ', e.data)
};
postMessage 与 onmessage
port中除了onmessage与postMessage方法外,还具有EventTarget的相关方法与开启和关闭控制方法。
port.onmessage(e => {
console.log('hello')
})
// 等价于 =>
port.addEventListener('message', e => console.log('hello'))
port.start();
window对象上的postMesagge一般有两种传参类型: window.postMessage(message[,options])
与window.postMessage(message,targetOrigin[,transfer])
。出于安全性考虑可以传入第二个参数限定目标域。而MessagePort的postMessage则不具有这个设置。
interface Window {
postMessage(message: any, targetOrigin: string, transfer?: Transferable[]): void;
}
interface MessagePort extends EventTarget {
postMessage(message: any, transfer: Transferable[]): void;
postMessage(message: any, options?: PostMessageOptions): void;
}
transfer对象
在postMessage中,除了消息本体message外,还可以传递transferable对象。transfer的transferable对象并不是被克隆的对象,而是类似于移交了控制权的对象,当transfer之后在当前的上下文中该对象则不再可用。
下面这些对象属于transferable对象:
- OffscreenCanvas (之前的文章中介绍过)
- ImageBitmap (也可以用在Canvas的数据传输中)
- MessagePort (消息通道通信中传递该对象)
- ArrayBuffer (原始二进制数据缓冲区,ws通信等的数据处理使用)
- 原文作者:yrq110
- 原文链接:http://yrq110.me/post/front-end/cross-domain-and-cross-document-communication/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。