跨文档通信(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通信等的数据处理使用)
  • 原文作者:
  • 原文链接: http://yrq110.me/post/front-end/cross-domain-and-cross-document-communication/
  • 版权声明: 本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
  •