广播通道 (Broadcast Channel)

概述

Broadcast Channel API 是浏览器原生 API,允许同源的不同浏览上下文(标签页、iframe、Worker)之间进行通信。

特点

  • 跨标签页通信:多个标签页间实时同步
  • 跨 iframe:主页面与 iframe 通信
  • 原生 API:无需第三方库
  • 自动数据同步:新标签页可请求现有数据
  • ⚠️ 仅限同源(same-origin)
  • ⚠️ 不支持 IE 浏览器

浏览器兼容性

浏览器支持版本
Chrome54+
Firefox38+
Safari15.4+
Edge79+
IE❌ 不支持

核心类

BroadcastBridge

封装了 BroadcastChannel,添加状态缓存和自动同步功能。

const bridge = new BroadcastBridge('my-channel')

// 发布(会同步到所有标签页)
bridge.publish('user', { id: 1, name: 'John' })

// 订阅
bridge.subscribe('user', (user) => {
  console.log('User updated:', user)
})

// 获取
const user = bridge.get('user')

// 清除
bridge.clear('user')

// 关闭
bridge.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

API 文档

推荐用法(BroadcastChannel 风格)

import {
  publish,
  get,
  clear,
  subscribe,
  unsubscribe
} from './BroadcastSingleton'
1
2
3
4
5
6
7

publish(key, data)

发布数据,同步到所有同源标签页。

// 标签页 A
publish('session', { userId: 123, token: 'xxx' })

// 标签页 B 会收到通知
1
2
3
4

get(key)

获取本地缓存的数据。

const session = get<Session>('session')
1

subscribe(key, callback)

订阅数据变化。新订阅时会自动向其他标签页请求同步数据。

subscribe('session', (session) => {
  if (session) {
    console.log('Session synced:', session)
  } else {
    console.log('Session cleared')
  }
})
1
2
3
4
5
6
7

unsubscribe(key, callback)

取消订阅。

unsubscribe('session', handler)
1

clear(key)

清除数据(会同步到其他标签页)。

// 用户登出时,清除所有标签页的 session
clear('session')
1
2

自动同步机制

┌─────────────────┐     publish     ┌─────────────────┐
│    标签页 A     │ ───────────────>│    标签页 B     │
│  (数据源)       │                 │  (已订阅)       │
└─────────────────┘                 └─────────────────┘
        │                                   ▲
        │                                   │
        │        sync-request/response      │
        │<──────────────────────────────────│
        │                                   │
        ▼                                   │
┌─────────────────┐                 ┌───────┴─────────┐
│    标签页 C     │                 │    新标签页 D   │
│  (已订阅)       │                 │  (请求同步)     │
└─────────────────┘                 └─────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14

当新标签页订阅数据时:

  1. 检查本地是否有缓存
  2. 如果没有,向其他标签页发送 sync-request
  3. 有数据的标签页响应 sync-response
  4. 新标签页收到数据并缓存

React Hook 示例

function useBroadcast<T>(key: string): T | undefined {
  const [data, setData] = useState<T | undefined>(() => get<T>(key))

  useEffect(() => {
    subscribe(key, setData)
    return () => unsubscribe(key, setData)
  }, [key])

  return data
}

// 使用:跨标签页同步登录状态
function LoginStatus() {
  const session = useBroadcast<Session>('session')

  if (!session) {
    return <div>未登录</div>
  }

  return <div>已登录: {session.userName}</div>
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

典型应用场景

1. 用户登录/登出同步

// 登录成功
function onLoginSuccess(session: Session) {
  publish('session', session)
}

// 登出
function onLogout() {
  clear('session')
}

// 所有标签页监听
subscribe('session', (session) => {
  if (!session) {
    // 跳转到登录页
    window.location.href = '/login'
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

2. 购物车同步

// 添加商品
function addToCart(item: CartItem) {
  const cart = get<CartItem[]>('cart') || []
  publish('cart', [...cart, item])
}

// 所有标签页的购物车图标会同步更新
1
2
3
4
5
6
7

3. 主题切换同步

// 切换主题
function toggleTheme() {
  const current = get<'light' | 'dark'>('theme') || 'light'
  publish('theme', current === 'light' ? 'dark' : 'light')
}

// 所有标签页同时切换主题
1
2
3
4
5
6
7

注意事项

  1. 同源限制:仅同源页面可通信
  2. 数据序列化:数据会被结构化克隆,不能传递函数
  3. 内存管理:页面卸载时会自动关闭通道
  4. 兼容性检查
if ('BroadcastChannel' in window) {
  // 支持 Broadcast Channel
} else {
  // 降级到其他方案(如 localStorage events)
}
1
2
3
4
5
上次更新:
(adsbygoogle = window.adsbygoogle || []).push({});