一、前言
最近需求有跨标签页操作,借此总结跨窗口(标签页)通信的常用方法,包括同源和跨域两种情况,给出的示例代码能让你快速上手。
二、同源
localStorage 配合 storage 事件:简单、兼容性好
window.addEventListener('storage', (e) => {
// 关键属性:key、oldValue、newValue、url
if (e.key != 'wc') return;
const data = JSON.parse(e.newValue);
console.log(data);
});
const data = {
a: 111,
now: Date.now(), // 保证数据变更,从而一定触发storage事件
};
localStorage.setItem('wc', JSON.stringify(data));
Broadcast Channel API:功能全
// 创建广播频道,也是“订阅”
const channel = new BroadcastChannel('test_channel');
// 接收频道的所有消息
channel.addEventListener('message', (e) => {
console.log(e.data);
});
// 消息无法反序列化时触发
channel.addEventListener('messageerror', (e) => {
console.error(e);
});
// 发送消息
channel.postMessage({
url: location.href,
data: '123',
});
// 断开频道连接
// channel.close();
三、跨域
利用 iframe 的 postmessage 接口
parent.html(注意修改子页面的 iframe 地址)
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Document</title>
<style>
#theIframe {
width: 450px;
height: 200px;
}
</style>
</head>
<body>
<h1>Parent</h1>
<button id="sendBtn">发送消息给子页面</button>
<p>收到消息:<span id="msg"></span></p>
<!-- 注意:修改为子页面地址 -->
<iframe src="http://localhost:7002/iframe/child.html" id="theIframe"></iframe>
<script>
// 子页面来源
const IFRAME_SOURCE = 'http://localhost:7002';
theIframe.addEventListener('load', () => {
try {
// 同源可以做任何事儿
theIframe.contentDocument.body.prepend("Hello, world!");
} catch (err) {
console.log('iframe 跨域');
}
sendBtn.addEventListener('click', () => {
// 发送消息到子页面
theIframe.contentWindow.postMessage({ command: 'show-text', text: '1' }, IFRAME_SOURCE);
});
});
// 接收来自子页面的消息
window.addEventListener('message', (e) => {
// 安全检查:忽略未知源的消息
if (e.origin !== IFRAME_SOURCE) return;
// 按消息类型做不同处理
const { command, text } = e.data;
switch (command) {
case 'show-text':
msg.textContent = text;
break;
}
});
</script>
</body>
</html>
child.html
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Document</title>
</head>
<body>
<h1>Child</h1>
<button id="sendBtn">发送消息给父页面</button>
<p>收到消息:<span id="msg"></span></p>
<script>
// 父页面来源
const PARENT_SOURCE = 'http://localhost:7001';
sendBtn.addEventListener('click', () => {
// 发送消息到父页面
window.parent.postMessage({ command: 'show-text', text: '3' }, PARENT_SOURCE);
});
// 接收来自父页面的消息并回应
window.addEventListener('message', (e) => {
if (e.origin !== PARENT_SOURCE) return;
const { command, text } = e.data;
switch (command) {
case 'show-text':
msg.textContent = text;
break;
}
// 回应
setTimeout(() => {
e.source.postMessage({ command: 'show-text', text: '2' }, e.origin);
}, 1000);
});
</script>
</body>
</html>
启动示例:
启动子页面服务:npx serve -l 7002
启动父页面服务:npx serve -l 7001
最后访问父页面即可。
三、结束
如果帮到你了,可以点个赞,欢迎交流沟通。