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>