浏览器原理 · 15/51
1. addEventListener 第三个参数 2. addEventListener 与 attachEvent 区别 3. 浏览器兼容性测试与内核 4. 浏览器兼容性问题 5. 浏览器内核与引擎 6. 浏览器图层创建条件 7. 浏览器多进程架构 8. 浏览器渲染机制 9. 浏览器存储方案 10. 浏览器版本检测方法 11. children 与 childNodes 区别 12. 常见浏览器兼容性问题 13. Chrome 页面进程数量 14. 坐标系统对比 15. 多标签页通讯方案 16. 删除 Cookie 17. 自定义事件 18. DOM 事件处理方式演进 19. 元素尺寸属性对比 20. DOM 节点操作 21. DOM 事件机制 22. addEventListener 与 attachEvent 的区别 23. 获取页面所有复选框 24. HTMLCollection 与 NodeList 区别 25. Hybrid 应用开发 26. 强缓存命中机制 27. 浏览器缓存机制 28. 页面编码与资源编码不一致处理 29. jQuery 事件绑定方法对比 30. Input 点击触发的事件顺序 31. JavaScript 浏览器兼容性问题 32. jQuery 多事件绑定实现 33. JSBridge 原理 34. 链接点击后 Hover 失效解决方案 35. 减少重绘和回流的性能优化 36. 移动端 300ms 点击延迟问题 37. 移动端视口配置 38. 移动端点击穿透问题解决 39. 移动端兼容性问题 40. JSBridge 原理与实现 41. 移动端 1px 像素问题解决方案 42. 浏览器渲染流程 43. 页面加载完成事件对比 44. Offset、Scroll、Client 属性对比 45. 同源策略与跨域解决方案 46. Script 标签位置对页面加载的影响 47. Service Worker 与 PWA 48. 存储方案对比:Cookie、Storage、IndexedDB 49. 强缓存默认时间 50. URL 到页面显示的完整过程 51. V8 引擎 JavaScript 执行过程

多标签页通讯方案

使用 localStorage、SharedWorker、BroadcastChannel 实现标签页间通信

问题

如何实现同源网页多标签页(Tab)之间的通讯?

解答

方案一:localStorage + storage 事件

利用 localStorage 变化时触发的 storage 事件实现跨标签页通信。

// 发送消息
function sendMessage(data) {
  localStorage.setItem('message', JSON.stringify({
    data,
    timestamp: Date.now() // 加时间戳确保每次都能触发事件
  }))
}

// 接收消息(其他标签页)
window.addEventListener('storage', (e) => {
  if (e.key === 'message' && e.newValue) {
    const { data } = JSON.parse(e.newValue)
    console.log('收到消息:', data)
  }
})

// 使用
sendMessage({ type: 'login', userId: 123 })

注意storage 事件只在其他标签页触发,当前页面不会触发。

方案二:BroadcastChannel

专门用于同源页面间广播通信的 API,使用最简单。

// 创建频道(所有标签页使用相同的频道名)
const channel = new BroadcastChannel('my-channel')

// 发送消息
channel.postMessage({ type: 'update', content: 'hello' })

// 接收消息
channel.onmessage = (e) => {
  console.log('收到消息:', e.data)
}

// 关闭频道
channel.close()

方案三:SharedWorker

多个标签页共享同一个 Worker 实例,通过它中转消息。

// shared-worker.js
const ports = []

self.onconnect = (e) => {
  const port = e.ports[0]
  ports.push(port)

  port.onmessage = (e) => {
    // 广播给所有连接的标签页
    ports.forEach(p => {
      if (p !== port) { // 不发给自己
        p.postMessage(e.data)
      }
    })
  }
}
// 页面中使用
const worker = new SharedWorker('shared-worker.js')

// 发送消息
worker.port.postMessage({ type: 'notify', msg: 'hello' })

// 接收消息
worker.port.onmessage = (e) => {
  console.log('收到消息:', e.data)
}

// 必须调用 start(如果没用 onmessage 而是用 addEventListener)
worker.port.start()

方案对比

方案优点缺点
localStorage兼容性好,简单只能传字符串,有容量限制
BroadcastChannelAPI 简洁,支持任意数据IE 不支持
SharedWorker可做复杂逻辑处理实现复杂,兼容性一般

其他方案

// 方案四:postMessage + window.open
// 适用于父页面打开子页面的场景
const child = window.open('child.html')
child.postMessage('hello', '*')

// 子页面接收
window.addEventListener('message', (e) => {
  console.log(e.data)
})

关键点

  • localStorage 的 storage 事件只在其他标签页触发,当前页面不触发
  • BroadcastChannel 是最简洁的方案,推荐优先使用
  • SharedWorker 适合需要共享状态或复杂逻辑的场景
  • 所有方案都要求同源(协议、域名、端口相同)
  • postMessage 可用于有引用关系的窗口间通信