跨标签页通讯
跨标签页通讯(Cross-Tab Communication)是指在浏览器中,不同标签页(或窗口)之间进行数据传递和通信的技术。常见的应用场景包括:
- 同步多个标签页的状态(如登录状态、主题设置等)。
- 在多个标签页之间共享数据。
- 实现广播机制,通知所有标签页某个事件的发生。
以下是几种常见的跨标签页通讯方法:
1. BroadcastChannel
BroadcastChannel 是浏览器原生支持的跨标签页通讯 API,允许在同一源(origin)下的不同标签页之间进行消息传递。
示例代码
发送消息
// 创建一个 BroadcastChannel 实例
const channel = new BroadcastChannel('my-channel');
// 发送消息
channel.postMessage({ type: 'greeting', message: 'Hello from Tab 1!' });
接收消息
// 创建一个 BroadcastChannel 实例
const channel = new BroadcastChannel('my-channel');
// 监听消息
channel.onmessage = (event) => {
console.log('Received message:', event.data);
};
特点
- 简单易用:API 设计简单,易于理解和使用。
- 同源限制:只能在相同源(协议 + 域名 + 端口)的标签页之间通信。
- 实时性:消息是实时传递的。
2. localStorage 和 storage 事件
通过 localStorage 和 storage 事件,可以在不同标签页之间共享数据。
示例代码
发送消息
// 将数据存储到 localStorage
localStorage.setItem('message', JSON.stringify({ type: 'greeting', message: 'Hello from Tab 1!' }));
接收消息
// 监听 storage 事件
window.addEventListener('storage', (event) => {
if (event.key === 'message') {
const message = JSON.parse(event.newValue);
console.log('Received message:', message);
}
});
特点
- 兼容性好:几乎所有浏览器都支持。
- 数据持久化:数据会持久化到
localStorage中。 - 事件触发:只有其他标签页修改
localStorage时才会触发storage事件,当前标签页不会触发。
3. SharedWorker
SharedWorker 是一种特殊的 Web Worker,允许多个标签页共享同一个 Worker 实例,从而实现跨标签页通信。
示例代码
创建 SharedWorker
// shared-worker.js
const ports = [];
onconnect = (event) => {
const port = event.ports[0];
ports.push(port);
port.onmessage = (event) => {
// 广播消息到所有连接的标签页
ports.forEach((p) => {
if (p !== port) {
p.postMessage(event.data);
}
});
};
};
发送和接收消息
// 创建 SharedWorker 实例
const worker = new SharedWorker('shared-worker.js');
// 发送消息
worker.port.postMessage({ type: 'greeting', message: 'Hello from Tab 1!' });
// 接收消息
worker.port.onmessage = (event) => {
console.log('Received message:', event.data);
};
特点
- 共享实例:多个标签页共享同一个 Worker 实例。
- 复杂场景:适合需要复杂逻辑处理的场景。
- 兼容性:部分浏览器可能不支持。
4. Window.postMessage
通过 Window.postMessage 可以在不同窗口或标签页之间传递消息。
示例代码
发送消息
// 打开一个新标签页
const newWindow = window.open('https://example.com');
// 向新标签页发送消息
newWindow.postMessage({ type: 'greeting', message: 'Hello from Tab 1!' }, 'https://example.com');
接收消息
// 监听 message 事件
window.addEventListener('message', (event) => {
if (event.origin === 'https://example.com') {
console.log('Received message:', event.data);
}
});
特点
- 跨域支持:可以在不同源的窗口或标签页之间通信。
- 安全性:需要指定目标窗口的
origin,确保消息来源可信。 - 窗口引用:需要持有目标窗口的引用(如
window.open返回的引用)。
5. Service Worker
Service Worker 可以拦截网络请求并缓存资源,同时也可以用于跨标签页通信。
示例代码
注册 Service Worker
// service-worker.js
self.addEventListener('message', (event) => {
// 广播消息到所有客户端
self.clients.matchAll().then((clients) => {
clients.forEach((client) => {
client.postMessage(event.data);
});
});
});
发送和接收消息
// 注册 Service Worker
navigator.serviceWorker.register('service-worker.js');
// 发送消息
navigator.serviceWorker.controller.postMessage({ type: 'greeting', message: 'Hello from Tab 1!' });
// 接收消息
navigator.serviceWorker.onmessage = (event) => {
console.log('Received message:', event.data);
};
特点
- 离线支持:Service Worker 可以用于离线缓存。
- 复杂场景:适合需要复杂逻辑处理的场景。
- 兼容性:部分浏览器可能不支持。
6. IndexedDB
IndexedDB 是一种浏览器端数据库,可以存储大量结构化数据。通过监听 IndexedDB 的变化,可以实现跨标签页通信。
示例代码
写入数据
const data = { type: 'greeting', message: 'Hello from Tab 1!' }
const dbName = "simpleDB";
const storeName = "SharedData";
const key = "sharedKey";
const request = indexedDB.open(dbName, 1);
request.onupgradeneeded = e => {
const db = e.target.result;
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName);
}
};
request.onsuccess = e => {
const db = e.target.result;
console.log("db", db);
const tx = db.transaction(storeName, "readwrite");
const store = tx.objectStore(storeName);
// 保存数据
store.put(data, key);
tx.oncomplete = () => {
// 关闭连接
db.close();
};
监听数据变化
getDBData() {
const dbName = "simpleDB";
const storeName = "SharedData";
const key = "sharedKey";
const request = indexedDB.open(dbName, 1);
request.onupgradeneeded = e => {
const db = e.target.result;
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName);
}
};
request.onsuccess = e => {
const db = e.target.result;
const tx = db.transaction(storeName, "readonly");
const store = tx.objectStore(storeName);
const getRequest = store.get(key);
getRequest.onsuccess = () => {
const data = getRequest.result;
db.close();
};
}
}
// 监听数据变化--这里使用setInterval
setInterval(() => {
this.getDB();
}, 2000);
// 版本号变化监听-onversionchange
db.onversionchange = () => {
console.log('数据库已更新,重新加载数据...');
}
特点
- 大数据支持:适合存储大量数据。
- 异步操作:所有操作都是异步的。
- 兼容性:几乎所有现代浏览器都支持。
注意问题: 1.无痕模式下无法使用indexedDB 2.取决于浏览器用户的主动行为,当用户拒绝访问时,不被允许
7. 总结
| 方法 | 特点 | 适用场景 | 兼容性说明 |
|---|---|---|---|
BroadcastChannel | 简单易用,实时性强,同源限制 | 同源标签页之间的实时通信 | 现代浏览器支持:Chrome 54+、Firefox 38+、Edge 79+、Safari 15.4+。IE 不支持。 |
localStorage | 兼容性好,数据持久化,事件触发 | 简单的状态同步 | 广泛支持:几乎所有浏览器都支持,包括 IE 8+。 |
SharedWorker | 共享实例,适合复杂场景 | 需要复杂逻辑处理的场景 | 部分支持:Chrome 4+、Firefox 29+、Edge 79+、Safari 5.1+。IE 和部分移动浏览器不支持。 |
Window.postMessage | 跨域支持,安全性高,需要窗口引用 | 跨域窗口或标签页之间的通信 | 广泛支持:几乎所有现代浏览器都支持,包括 IE 8+。 |
Service Worker | 离线支持,适合复杂场景 | 需要离线支持的场景 | 现代浏览器支持:Chrome 40+、Firefox 44+、Edge 17+、Safari 11.1+。IE 不支持。 |
IndexedDB | 大数据支持,异步操作 | 需要存储大量数据的场景 | 广泛支持:Chrome 11+、Firefox 4+、Edge 12+、Safari 8+。IE 10+ 部分支持。 |
选择建议
- 简单场景:优先选择
localStorage或BroadcastChannel,兼容性好且易于实现。 - 跨域通信:使用
Window.postMessage。 - 复杂场景:选择
SharedWorker或Service Worker。 - 大数据存储:使用
IndexedDB。