postMessage填坑——跨域页面信息传输

107 阅读2分钟

前端缓存无论locastorage、sessionstorage、indexedDB,都只能在本域进行数据共享,如果要跨页面进行信息传输,那就得另辟蹊径。

  • 思路1:在url上加参数
    这种方式最简便,但是url长度是有限制的,数据太多就不得行了;
  • 思路2:写个接口传输
    这种方式太笨重,肯定不考虑啊
  • 思路3:用window.postMessage
    got it! window.postMessage方法可以安全地实现跨源通信。

先来模拟一下需求:有两个项目A和B,产品经理希望:

  1. 在A点个按钮可以打开B
  2. A要把大量的数据传给B,让B一进入页面就能用数据进行业务操作
  3. B接收了信息要回复A已确认收到

假设项目A端口是3001,项目B端口是3002
项目A代码如下:

<script setup>
import { ref } from "vue";

const bReply = ref("");

const gotoB = () => {
    const targetPage = window.open("http://127.0.0.1:3002/", "/"); // 解决需求1
    targetPage.postMessage(
      { msg: { ··· /* 这里是很大的数据量*/ } },
      "http://127.0.0.1:3002"
    ); // 解决需求2,发送信息
};

// 解决需求3,监听回复消息
const receiveMessage = (event) => {
  if (event.data) {
    bReply.value = event.data;
  }
};
window.addEventListener("message", receiveMessage, false);
</script>

<template>
  <h1>项目A</h1>
  <button type="button" @click="gotoB">打开跨域项目B</button>
  <div>项目B回复:{{ bReply }}</div>
</template>

项目B代码如下:

<script setup>
import { ref, onMounted } from "vue";

let msg = ref("");

const receiveMessage = (event) => {
  msg.value = event.data.msg;
  event.source.postMessage("我已收到", event.origin); // 解决需求3,回复消息
};
window.addEventListener("message", receiveMessage, false); // 解决需求2,接收信息
</script>

<template>
  <h1>项目B</h1>
  <div>项目A传来的数据:{{ JSON.stringify(msg) }}</div>
</template>
</style>

以上代码就能实现跨域页面传输信息了,但是还存在两个问题:

  1. 如果没有限制接收消息源,接收方可以收到所有发送方的信息,这样会有安全问题,所以要限制消息源。
    receiveMessage方法里增加event.origin判断,A和B的receiveMessage都要添加。
const receiveMessage = (event) => {
if (event.origin !== "http://127.0.0.1:3000") return; // ⚠️添加限制
  msg.value = event.data.msg;
  event.source.postMessage("我已收到", event.origin); // 解决需求3,回复消息
};
  1. 以上代码跑起来会发现项目B有一定概率接收不到数据,这是因为异步执行的原因,所以应该在发送方法中添加延迟
const gotoB = () => {
    const targetPage = window.open("http://127.0.0.1:3002/", "/");
    setTimeout(() => {
        targetPage.postMessage(
          { msg: { ··· /* 这里是很大的数据量*/ } },
          "http://127.0.0.1:3002"
        );
    }, 1000);
};

last but not least,postMessage的浏览器兼容性很好

image.png