iframe
iframe是什么
<iframe>
是 HTML 提供的"页中页"标签,它能在当前文档内嵌入一个独立的浏览上下文,拥有自己完整的 window、DOM、CSS 和 JavaScript 运行环境,天然与父页面隔离;借助 postMessage、sandbox、allow 等 API 与安全属性,开发者既可在跨域场景下安全地通信与限制权限,又能通过懒加载、预连接等手段优化性能,因此常被用于第三方内容嵌入、微前端子应用、支付收银台、在线文档预览等需要"隔离 + 互动"的业务场景。
应用场景
- iframe 把"独立页"嵌入主文档,天然隔离又支持 postMessage 通信,因此常用来加载第三方支付页、微前端子应用、跨域上传组件、在线文档预览、广告或地图等需要安全沙箱、样式隔离、懒加载的模块,既避免脚本冲突,又能随取随用、按需销毁。
通信方式
同源
- 父页面和 iframe 的源(协议 + 域名 + 端口)完全相同,因此浏览器允许它们直接互相访问对方的 document、window 和 DOM 节点,无需任何消息转发或 postMessage。
例如下面代码块
js
// 父页拿到子页的 document
const childDoc = window.frames[0].document;
childDoc.body.style.background = '#000'; // 直接改样式
// 子页拿到父页的 DOM
window.parent.document.getElementById('header').innerText = 'Hi from iframe';
跨域
- 使用PostMessage进行跨域通信
- 双方先 window.addEventListener('message', handler) 注册监听器;
- 发消息用 otherWindow.postMessage(data, origin),data 最好是结构化克隆对象,origin 必须精确匹配,避免 "*";
例如下列代码块:
js
if (window.parent !== window) {
// 发送数据到父页面
window.parent.postMessage({
type: 'mock_data',
data: mockData,
source: 'iframe'
}, '*'); // 第二个参数是目标origin,'*'表示任何origin
console.log('数据已发送到父页面:', mockData);
alert('数据已成功发送到父页面!');
} else {
alert('当前页面不是iframe,无法发送数据到父页面');
}
视频演示
- 下面这两个网页实现了一个iframe父子页面通信的演示系统。子页面(iframe.html)负责生成包含用户名、用户ID、消息内容和时间戳的模拟数据,并通过点击"发送数据到父页面"按钮,使用postMessage API将数据发送给父页面。父页面(parent.html)则通过监听message事件来接收来自子页面的数据,并以美观的卡片形式在页面上方展示接收到的用户信息、消息内容和时间戳,实现了跨域iframe之间的安全数据通信功能。
代码部分
- 父页面代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>iframe父页面</title>
</head>
<style>
#parent {
background-color: aqua;
}
iframe {
width: 80vw;
height: 80vh;
}
</style>
<body>
<div id="parent">
<h1>这是父页面</h1>
<div id="received-data">等待接收数据...</div>
<iframe src="iframe.html" frameborder="0"></iframe>
</div>
</body>
<script>
// 接收来自iframe的消息
window.addEventListener("message", function (event) {
// 验证消息来源(可选,但推荐)
// if (event.origin !== '期望的origin') return;
const data = event.data;
// 检查消息类型
if (data.type === "mock_data" && data.source === "iframe") {
console.log("父页面收到数据:", data.data);
// 显示接收到的数据
displayReceivedData(data.data);
}
});
// 显示接收到的数据
function displayReceivedData(mockData) {
const container = document.getElementById("received-data");
let html = "<h3>从子页面接收到的Mock数据:</h3>";
mockData.forEach((item, index) => {
html += `
<div class="received-message">
<strong>消息 ${index + 1}:</strong><br>
<strong>用户:</strong> ${item.userName} (ID: ${item.userId})<br>
<strong>消息内容:</strong> ${item.message}<br>
<strong>时间:</strong> ${item.timestamp}
</div>
`;
});
html += `<div class="data-info">共收到 ${mockData.length} 条消息</div>`;
container.innerHTML = html;
// 滚动到最新数据
container.scrollTop = container.scrollHeight;
}
</script>
</html>
iframe子页面代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>iframe子页面</title>
</head>
<style>
#iframe {
background-color: rgb(255, 204, 0);
width: 80vw;
height: 80vh;
}
</style>
<body>
<div id="iframe">
<h1>这是子页面</h1>
<div id="messages-container"></div>
<button id="send-btn">发送</button>
</div>
</body>
<script>
// Mock数据生成函数
function generateMockData() {
const users = [
{ id: 1001, name: "张三" },
{ id: 1002, name: "李四" },
{ id: 1003, name: "王五" },
{ id: 1004, name: "赵六" },
{ id: 1005, name: "钱七" },
];
const messages = [
"你好,今天天气不错!",
"这个功能看起来很棒!",
"我同意你的观点",
"有什么新的更新吗?",
"感谢分享这些信息",
"期待下次的会议",
"这个设计很漂亮",
"代码运行得很顺利",
"需要帮忙吗?",
"完成得很棒!",
];
const mockData = [];
// 生成10条模拟消息
for (let i = 0; i < 10; i++) {
const randomUser = users[Math.floor(Math.random() * users.length)];
const randomMessage =
messages[Math.floor(Math.random() * messages.length)];
// 生成随机时间戳(最近7天内)
const randomDaysAgo = Math.floor(Math.random() * 7);
const randomHoursAgo = Math.floor(Math.random() * 24);
const randomMinutesAgo = Math.floor(Math.random() * 60);
const timestamp = new Date();
timestamp.setDate(timestamp.getDate() - randomDaysAgo);
timestamp.setHours(timestamp.getHours() - randomHoursAgo);
timestamp.setMinutes(timestamp.getMinutes() - randomMinutesAgo);
mockData.push({
userId: randomUser.id,
userName: randomUser.name,
message: randomMessage,
timestamp: timestamp.toLocaleString("zh-CN"),
});
}
return mockData;
}
// 显示mock数据
function displayMockData() {
const container = document.getElementById('messages-container');
const mockData = generateMockData();
mockData.forEach(data => {
const messageDiv = document.createElement('div');
messageDiv.className = 'message';
messageDiv.innerHTML = `
<div>
<span class="user-info">${data.userName} (ID: ${data.userId})</span>
<span class="timestamp">${data.timestamp}</span>
</div>
<div class="message-content">${data.message}</div>
`;
container.appendChild(messageDiv);
});
}
// 通过postMessage发送数据到父页面
function sendDataToParent() {
const mockData = generateMockData();
// 获取当前iframe的window.parent(父窗口)
if (window.parent !== window) {
// 发送数据到父页面
window.parent.postMessage({
type: 'mock_data',
data: mockData,
source: 'iframe'
}, '*'); // 第二个参数是目标origin,'*'表示任何origin
console.log('数据已发送到父页面:', mockData);
alert('数据已成功发送到父页面!');
} else {
alert('当前页面不是iframe,无法发送数据到父页面');
}
}
// 页面加载完成后显示数据
document.addEventListener('DOMContentLoaded', displayMockData);
// 设置发送按钮点击事件
const sendButton = document.getElementById('send-btn');
sendButton.addEventListener('click', sendDataToParent);
</script>
</script>
</html>