跨标签页通讯

242 阅读5分钟

跨标签页通讯

跨标签页通讯(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. localStoragestorage 事件

通过 localStoragestorage 事件,可以在不同标签页之间共享数据。

示例代码

发送消息
// 将数据存储到 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