Skip to content
📈0️⃣

iframe 中父子页面交互之——父页面

接收消息实测

接收消息实测参考后续章节 父子页面交互 1-父页面-实战

在 Web 开发中,使用 iframe 嵌套页面是一种常见的技术手段。在父页面中通过 iframe 与嵌套的子页面进行通信,实现父子页面之间的交互。父页面接收子页面消息并做出交互的步骤如下:

父页面逻辑实现

1. 准备工作

这里使用 Vue 3 来实现父页面与子页面之间的通信。确保已经安装好 Vue 3 相关的环境。

2. 实现父页面逻辑

a. 引入所需组件和依赖

  • baseUrl: 定义接口请求的基础地址
js
import { ref, onMounted } from "vue";

const baseUrl = import.meta.env.DEV
  ? "http://localhost:5173" // 开发环境 http://localhost:5173
  : "https://zichin.com"; // 生产环境 https://zichin.com
  • url 和 src: 控制 iframe 的加载链接
js
const url = ref(
  `${baseUrl}/blog/4.跨域交互/2.实战-postmessage/4.父子页面交互2-子页面-实战.html`
);
const src = ref(
  `${baseUrl}/blog/4.跨域交互/2.实战-postmessage/4.父子页面交互2-子页面-实战.html`
);
  • DATA_MAP: 父页面里存储的处理函数和数据(mock 数据)
js
DATA_MAP = {
  getUserInfo: () => ({ name: "zichin", age: 22 }),
  jump: (url) => {
    window.open(url);
  },
  userInfo: { name: "zichin", age: 23 },
  title: window.document.title,
};
  • doMessage:处理接收到的消息
js
const url = ref(
  `${baseUrl}/blog/4.跨域交互/2.实战-postmessage/4.父子页面交互2-子页面-实战.html`
);
const src = ref(
  `${baseUrl}/blog/4.跨域交互/2.实战-postmessage/4.父子页面交互2-子页面-实战.html`
);

b. 注册消息事件监听器:

通过 onMounted 钩子函数,在组件挂载后注册了消息事件监听器,用来接收子页面发送过来的消息,并做出相应处理。

js
const doMessage = async (e) => {
  // 判断消息来源是否合法
  const whiteList = [
    "http://localhost:8080",
    "http://localhost:8082",
    "http://localhost:8886",
    "http://localhost:8888",
    "http://localhost:5173",
    "http://www.zichin.com",
    "https://www.zichin.com",
    "http://zichin.com",
    "https://zichin.com",
  ];
  if (!whiteList.includes(e.origin)) {
    console.warn("消息来源地址不合法, 请联系开发人员", e.origin);
    return;
  }
  const data = e.data;
  // 处理接收到的消息...
};
onMounted(() => {
  // ...
  window.removeEventListener("message", doMessage, false);
  window.addEventListener("message", doMessage, false);
});

3. 页面渲染部分

a. 模板结构:在模板中展示了一个简单的测试 iframe 的容器,包括一个输入框和一个按钮用来加载指定链接的 iframe。

html
<div class="test-iframe-container">
  <div class="title">
    <el-input v-model="url" style="width: 80%" />
    <el-button @click="src = url">load</el-button>
  </div>

  <div class="box">
    <iframe :src="src" width="80%" height="300px"></iframe>
  </div>
</div>

b. 样式定义:通过样式的设置,使得页面看起来更加美观且布局合理。

css
.test-iframe-container {
  background-color: #ebf0ff;
  padding: 10px;
  line-height: 2;
  width: 100%;

  .title {
    text-align: center;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 10px;
  }

  .box {
    display: flex;
    justify-content: center;
    align-items: center;

    iframe {
      background-color: #ccc;
      border: 2px solid silver;
    }
  }
}

父页面代码实现

TestIframe.vue 组件代码实现

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

const baseUrl = import.meta.env.DEV
  ? "http://localhost:5173" // 开发环境 http://localhost:5173
  : "https://zichin.com"; // 生产环境 https://zichin.com

const url = ref(
  `${baseUrl}/blog/4.跨域交互/2.实战-postmessage/4.父子页面交互2-子页面-实战.html`
);
const src = ref(
  `${baseUrl}/blog/4.跨域交互/2.实战-postmessage/4.父子页面交互2-子页面-实战.html`
);
let DATA_MAP;
const doMessage = async (e) => {
  const whiteList = [
    "http://localhost:8080",
    "http://localhost:8082",
    "http://localhost:8886",
    "http://localhost:8888",
    "http://localhost:5173",
    "http://www.zichin.com",
    "https://www.zichin.com",
    "http://zichin.com",
    "https://zichin.com",
  ];
  if (!whiteList.includes(e.origin)) {
    console.warn("消息来源地址不合法, 请联系开发人员", e.origin);
    return;
  }
  const data = e.data;
  const foo = data.foo || data.fn;
  const postName = data.src;
  const params = JSON.parse(decodeURIComponent(data.params || "{}"));
  console.log(`从 ${e.origin} 收到消息...', message源为: ${postName}`);
  if (!foo) {
    return;
  }
  const result = { success: true };
  if (typeof DATA_MAP[foo] === "function") {
    result.data = DATA_MAP[foo](params);
  } else {
    result.data = DATA_MAP[foo];
  }
  const iframe = document.querySelector(`iframe[src^="${e.origin}"]`);
  if (iframe) {
    console.log("返回消息结果", result);
    iframe.contentWindow.postMessage(
      {
        fn: foo,
        params: encodeURIComponent(JSON.stringify(result)),
        src: postName || "zc-fuzi-test",
      },
      e.origin
    );
  }
};
onMounted(() => {
  DATA_MAP = {
    getUserInfo: () => ({ name: "zichin", age: 22 }),
    jump: (url) => {
      window.open(url);
    },
    userInfo: { name: "zichin", age: 23 },
    title: window.document.title,
  };
  window.removeEventListener("message", doMessage, false);
  window.addEventListener("message", doMessage, false);
});
</script>
vue
<template>
  <div class="test-iframe-container">
    <div class="title">
      <el-input v-model="url" style="width: 80%" />
      <el-button @click="src = url">load</el-button>
    </div>

    <div class="box">
      <iframe :src="src" width="80%" height="300px"></iframe>
    </div>
  </div>
</template>
vue
<style lang="scss">
.test-iframe-container {
  background-color: #ebf0ff;
  padding: 10px;
  line-height: 2;
  width: 100%;

  .title {
    text-align: center;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 10px;
  }

  .box {
    display: flex;
    justify-content: center;
    align-items: center;

    iframe {
      background-color: #ccc;
      border: 2px solid silver;
    }
  }
}
</style>