跨文档通信

跨文档通信

跨文档通信指的是从页面A到页面B之间的通信,不需要借助服务器

能够实现的方法:

  1. 通过消息事件实现,不受跨域的限制,并且可实现双向通信
    • postMessage
    • MessageChannel
  2. 通过客户端存储实现,收到跨域的限制(只有同源的文档才可以访问同一块客户端存储)
    • cookie
    • localStrage/sessionStroage
    • IndexedDB
  3. 在页面跳转时携带信息,不受跨域的限制,但是只能单向、单次通信
    • window.name
    • window.location.hash

postMessage方法

postMessage方法可以实现两个页面的跨文档通信,不受跨域的限制

发送:

otherWindow.postMessage(message, targetOrigin, [transfer]);
  1. 事件的发送方是otherWindow,它是其他窗口的引用,比如iframe的contentWindow对象,或者是window.open返回的窗口对象
  2. message会被自动序列化,所以可以发送字符串,也可以发送对象
  3. targetOrigin可以是*,表明无限制,或者是一个URI,发送消息的时需要目标窗口的协议、主机地址、端口三者都匹配才能成功。如果设置为*有可能导致数据泄露
  4. transfer用的不多,它会和message一同传递,这些对象的所有权将被移交给接收方,发送一方不再保有所有权。后面在MessageChannel会用到。

window.location.origin返回的就是URL协议+服务器名称+端口号

接受:

window.addEventListener("message", (e) => {}, false);

监听的message事件,其事件对象e包含的属性有:

  1. data,从其他window中传过来的对象
  2. origin,消息来源的窗口的origin,这个字符串也是由URL协议+服务器名称+端口号拼接而成,用来验证消息来源
  3. source,对消息来源的窗口的引用,可以通过这个属性调用postMessage方法回应消息,实现双向通信

例子:

Tab1.html:

<body>
  <button id="btn1">打开新窗口</button>
  <h1 id="h1"></h1>
  <button id="btn2">发送消息</button>
  <script>
    const btn1 = document.querySelector('#btn1');
    const btn2 = document.querySelector('#btn2');
    const h1 = document.querySelector('#h1');

    /**
     * 通过postMessage进行通信
     */
    let otherWindow;

    // 打开Tab2
    btn1.addEventListener('click', () => {
      otherWindow = window.open('./demo30-2.html');
    });

    // 向Tab2发送消息
    btn2.addEventListener('click', () => {
      // postMessage的调用者是其他窗口的引用,比如iframe的contentWindow属性,window.open返回的窗口对象
      // 指定目标窗口的targetOrigin
      otherWindow.postMessage('hello from tab 1', window.location.origin);
      console.log('发送成功')
    });

    // 监听message事件
    window.addEventListener('message', e => {
      // 判断消息来源
      if (e.origin !== window.location.origin) {
        return
      }
      h1.innerHTML += e.data.value + '<br>';
    })
  </script>
</body>

Tab2:

<body>
  <h1 id="h1"></h1>
  <script>
    const h1 = document.querySelector('#h1');
    /**
     * 通过postMessage进行通信
     */
    // 监听message事件
    window.addEventListener('message', e => {
      // 验证消息来源
      if (e.origin !== window.location.origin) {
        return
      }
      h1.innerHTML += e.data + '<br>';
      setTimeout(() => {
        // e.source是消息来源的窗口的引用
        e.source.postMessage({value: 'answer from tab2'}, window.location.origin)
      }, 3000)
    })
  </script>
</body>

MessageChannel

MessageChannel实际上也利用了postMessage的方法,通过实例化MessageChannel对象,相当于建立了一个消息管道,实例有两个属性port1port2,相当于管道的两个出口,我们利用这两个出口就可以实现跨文档通信

在当前文档,我们可以访问port1,目标文档如何获得port2呢?那就是通过前面提到的postMessage的第四个参数transfer,将port2传递给目标文档,目标文档通过监听message事件,事件对象eport属性中就可以获得管道的另一个出口

在建立管道的过程需要获取目标文档,使用的是文档窗口的message相关事件,待管道建立后就只需要使用管道的消息事件就可以了。通过port.postMessageport.onmessage就可以实现跨文档通信

Tab1:

<body>
  <button id="btn1">打开新窗口</button>
  <h1 id="h1"></h1>
  <button id="btn2">发送消息</button>
  <script>
    const btn1 = document.querySelector('#btn1');
    const btn2 = document.querySelector('#btn2');
    const h1 = document.querySelector('#h1');
    let otherWindow;

    /**
     * MessageChannel
     */
    const channel = new MessageChannel();
    const {port1, port2} = channel;

    btn1.addEventListener('click', () => {
      // 新建页面
      otherWindow = window.open('./demo30-2.html');
    });

    btn2.addEventListener('click', () => {
      // 将port2传递给目标文档
      otherWindow.postMessage('try to connect', window.location.origin, [port2])
    });

    // 监听port的message事件
    port1.onmessage = function (e) {
      h1.innerHTML += e.data + '<br>';
      setTimeout(() => {
        // 通过管道发送消息
        port1.postMessage('Hello form tab1')
      }, 2000);
    }
  </script>

Tab2:

<body>
  <h1 id="h1"></h1>
  <script>
    const h1 = document.querySelector('#h1');
   
    /**
     * MessageChannel
     */
    let port;

    // 监听message事件,建立管道
    window.addEventListener('message', e => {
      // 验证消息来源
      if (e.origin !== window.location.origin) {
        return
      }
      h1.innerHTML += e.data + '<br>';

      // 获取转移过来的端口
      port = port || e.ports[0];

      // 监听port的message事件
      port.onmessage = function (e) {
        h1.innerHTML += e.data + '<br>';
        // 通过管道发送消息
        setTimeout(() => {
          port.postMessage('answer from tab2')
        }, 2000)
      };

      //
      setTimeout(() => {
        port.postMessage('connection is ok')
      }, 2000)
    });
  </script>

cookie/localStorage/sessionStroage

这三者本质是相同的,都可以在浏览器端进行保存并且访问

cookie本质上是服务器发送到浏览器的数据,在使用这三者进行跨文档通信收到跨域的限制。

cookie可以通过document.cookie直接写入和读取,localStoragesessionStroage有专门的API:

window.localStorage.setItem(key, value)
window.localStorage.getItem(key)

它们都只能存入字符串,如果需要存入对象,则需要手动将对象序列化。

IndexedDB

IndexedDB用于客户端存储大量结构化数据,是一个机遇JavaScript的面向对象的数据库。有了它就可以不考虑网络的可用性,创建可离线的Web应用程序。

具体的使用以后慢慢学习吧。

IndexedDB@MDN
基本概念@MDN
使用 IndexedDB@MDN

window.name

当通过更改window.location.href或者<a>标签在当前窗口载入新的页面时,window.name会保留上个页面设置的信息

Tab1:

<body>
  <a href="demo30-2.html">打开页面</a>
  <script>
    window.name = '123';
  </script>
</body>

Tab2:

window.onload = function () {
  console.log(window.name)
}
// 123

注意,这种方式必须是在当前页面打开的文档

window.location.hash

可以将要传递的信息存储在window.location.hash

Tab1:

<a href="demo30-2.html#hello">打开页面</a>

Tab2:

window.onload = function () {
  console.log(window.location.hash.slice(1))
}

注意,这种形式有可能会导致页面滚动到具有重名的标示符,同时会在浏览器的访问历史用增加一条记录

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值