前端跨页面通讯终极指南②:BroadcastChannel 用法全解析
前言
上一篇介绍了PostMessage跨页面通讯的方式。有没有一种更简洁的方式,兄弟页面也能像父子页面一样通讯。今天就介绍一个更高效、更简洁的方案——BroadcastChannel API,它能轻松搞定父子、子父、兄弟页面间的通讯。
1. BroadcastChannel是什么?
BroadcastChannel 接口表示给定源的任何浏览上下文都可以订阅的命名频道。它允许同源的不同浏览器窗口、标签页、frame 或者 iframe 下的不同文档之间相互通信。消息通过 message 事件进行广播,该事件在侦听该频道的所有 BroadcastChannel 对象上触发,发送消息的对象除外。
简单来说,它就像一个“无线电台”,多个页面只要订阅了同一个“频道”(指定相同的频道名称),就能接收该频道发送的所有消息,实现数据的双向流转。
需要注意,BroadcastChannel仅支持同源页面,兼容性略低,需要搭配其他方案作为降级处理。
2. 如何使用
BroadcastChannel的使用流程如下:
- 创建频道
- 订阅消息
- 发送消息
- 关闭订阅
2.1 创建频道(主题)
通过 new BroadcastChannel('channel-name')创建一个“频道”(相当于发布-订阅中的“主题”)。所有加入同一频道的上下文,共享同一个通信通道。
// 1. 创建/订阅指定名称的频道(关键:多页面频道名必须一致)
const channel = new BroadcastChannel('my-channel');
2.2 订阅消息(监听)
通过监听 message事件订阅该频道的消息(相当于订阅者注册回调):
channel.onmessage = (e) => {
console.log('收到消息:', e.data); // e.data 就是发送的消息内容
// 根据消息类型执行对应逻辑
if (e.data.type === 'refresh') {
// 执行刷新操作
}
};
2.3 发布消息(发送)
通过 postMessage()向频道发送消息(相当于发布者触发发布):
channel.postMessage({
type: 'refresh',
data: { id: 123 }
});
2.4 退订(关闭)
通过 close()方法离开频道(可选,浏览器通常会在上下文销毁时自动清理):
window.addEventListener('beforeunload', () => {
channel.close();
});
// 1. 创建/订阅指定名称的频道(关键:多页面频道名必须一致)
const channel = new BroadcastChannel('my-channel');
// 2. 接收消息
channel.onmessage = (e) => {
console.log('收到消息:', e.data); // e.data 就是发送的消息内容
// 根据消息类型执行对应逻辑
if (e.data.type === 'refresh') {
// 执行刷新操作
}
};
// 3. 发送消息(支持字符串、对象等多种数据类型)
channel.postMessage({
type: 'refresh',
data: { id: 123 }
});
// 4. 关闭频道(页面卸载时调用,避免内存泄漏)
window.addEventListener('beforeunload', () => {
channel.close();
});
3、实践场景
下面我们针对前端最常见的父子页面(父窗口打开子窗口)、子父页面(子窗口向父窗口反馈)、兄弟页面(同一父页面打开的多个子窗口)三种场景,分别给出具体的实现代码。
3.1 父子通讯
父页面点击“刷新子页面”按钮,子页面接收到指令后刷新数据。
父页面代码(打开子窗口并发送消息):
// 1. 创建频道
const parentChannel = new BroadcastChannel('parent-child-channel');
// 2. 点击按钮向子窗口发送消息
document.getElementById('refreshChildBtn').addEventListener('click', () => {
parentChannel.postMessage({
type: 'refresh-data',
message: '父页面指令:请刷新数据'
});
});
// 3. 页面卸载时关闭频道
window.addEventListener('beforeunload', () => {
parentChannel.close();
});
子页面代码(接收父页面消息并执行操作):
// 1. 订阅同一个频道(频道名必须和父页面一致)
const childChannel = new BroadcastChannel('parent-child-channel');
// 2. 接收父页面消息
childChannel.onmessage = (e) => {
const { type, message } = e.data;
if (type === 'refresh-data') {
// 显示接收的消息
document.getElementById('message').textContent = message;
// 执行刷新数据的逻辑
refreshData();
}
};
// 刷新数据的核心函数
function refreshData() {
// 模拟请求接口刷新数据
console.log('子页面正在刷新数据...');
// 这里写具体的刷新逻辑
}
// 3. 页面卸载时关闭频道
window.addEventListener('beforeunload', () => {
childChannel.close();
});
3.2 子父通讯(子窗口向父窗口反馈结果)
需求:子页面完成表单提交后,向父页面发送“提交成功”的消息,父页面接收到后关闭子窗口并刷新自身数据。
子页面代码(提交表单后发送消息):
// 1. 订阅频道(和父页面保持一致)
const childChannel = new BroadcastChannel('child-parent-channel');
// 2. 表单提交逻辑
document.getElementById('submitForm').addEventListener('submit', async (e) => {
e.preventDefault();
try {
// 模拟表单提交接口请求
await submitFormData();
// 提交成功后向父页面发送消息
childChannel.postMessage({
type: 'submit-success',
data: { formId: 456, status: 'success' }
});
// 延迟关闭子窗口,确保消息发送完成
setTimeout(() => {
window.close();
}, 300);
} catch (error) {
console.error('提交失败:', error);
}
});
// 3. 页面卸载时关闭频道
window.addEventListener('beforeunload', () => {
childChannel.close();
});
父页面代码(接收子页面消息并执行操作):
// 1. 创建频道
const parentChannel = new BroadcastChannel('child-parent-channel');
// 2. 接收子页面消息
parentChannel.onmessage = (e) => {
const { type, data } = e.data;
if (type === 'submit-success') {
console.log('子页面表单提交成功:', data);
// 执行父页面刷新逻辑
parentRefreshData();
// (可选)关闭子窗口(如果子窗口未自行关闭)
// childWindow.close();
}
};
// 父页面刷新数据函数
function parentRefreshData() {
console.log('父页面正在刷新数据...');
// 具体刷新逻辑
}
// 3. 页面卸载时关闭频道
window.addEventListener('beforeunload', () => {
parentChannel.close();
});
3.3 兄弟通讯(多个子窗口间同步状态)
需求:父页面打开两个子窗口A和B,当子窗口A修改数据后,子窗口B实时同步更新数据。
子窗口A代码(修改数据后发送消息):
// 1. 订阅兄弟通讯频道
const brotherChannel = new BroadcastChannel('brother-channel');
// 2. 模拟修改数据操作
document.getElementById('updateDataBtn').addEventListener('click', () => {
const newData = {
id: 789,
content: '子窗口A修改后的新内容',
updateTime: new Date().toLocaleString()
};
// 保存修改后的数据
saveData(newData);
// 向频道发送消息,通知其他兄弟窗口
brotherChannel.postMessage({
type: 'data-updated',
newData: newData
});
});
// 3. 页面卸载时关闭频道
window.addEventListener('beforeunload', () => {
brotherChannel.close();
});
子窗口B代码(接收消息并同步数据):
// 1. 订阅同一个兄弟通讯频道
const brotherChannel = new BroadcastChannel('brother-channel');
// 2. 接收子窗口A发送的消息
brotherChannel.onmessage = (e) => {
const { type, newData } = e.data;
if (type === 'data-updated') {
console.log('收到子窗口A的更新消息:', newData);
// 同步更新页面数据展示
document.getElementById('dataContent').textContent = newData.content;
document.getElementById('updateTime').textContent = newData.updateTime;
}
};
// 3. 页面卸载时关闭频道
window.addEventListener('beforeunload', () => {
brotherChannel.close();
});
接收输入如下:
![]()
4. 总结
最后总结一下:对比传统的postMessage跨页面通讯方案,BroadcastChannel兄弟页面无需转发,几行代码就能轻松实现通讯。