跨分頁即時通訊:Broadcast Channel API 實戰
在 Web 開發中,我們常遇到一個棘手問題:使用者在「分頁 A」登出了,但在「分頁 B」卻還顯示登入狀態,導致後續操作失敗。或者使用者在一個分頁切換了「深色模式」,其他分頁卻沒有同步跟進。
Broadcast Channel API 正是為此而生。它允許我們在 同源 (Same-Origin) 的不同瀏覽上下文(分頁、視窗、iframe、Worker)之間建立一個廣播頻道,實現簡單高效的一對多通訊。
一、 API 核心概念
1.1 基本語法
使用方式就像收音機頻道一樣:
javascript
// A 分頁:建立頻道並發送訊息
const channel = new BroadcastChannel('app_channel');
channel.postMessage({ type: 'LOGOUT' });
// B 分頁:加入同一頻道並接收訊息
const channel = new BroadcastChannel('app_channel');
channel.onmessage = (event) => {
if (event.data.type === 'LOGOUT') {
console.log('收到登出指令');
}
};1.2 與其他方案比較
| 方案 | 特點 | 缺點 |
|---|---|---|
| Broadcast Channel | 專為通訊設計,API 簡單,延遲極低。 | 不支援舊版瀏覽器 (IE)。 |
| localStorage | 透過 storage 事件監聽。 | 語意不明確,儲存容量有限制。 |
| SharedWorker | 可共享狀態與邏輯。 | 實作複雜度高。 |
二、 封裝 useBroadcastChannel Composable
我們將封裝一個 useBroadcastChannel,讓訊息的收發變得具備響應式特性,並自動管理資源釋放。
2.1 介面定義
typescript
export interface UseBroadcastChannelReturn<T> {
isSupported: boolean;
data: Ref<T | null>;
post: (data: T) => void;
close: () => void;
error: Ref<MessageEvent | null>;
}2.2 完整實作
typescript
// composables/useBroadcastChannel.ts
import { ref, onMounted, onUnmounted } from 'vue';
export function useBroadcastChannel<T = any>(name: string) {
const isSupported = typeof window !== 'undefined' && 'BroadcastChannel' in window;
const data = ref<T | null>(null);
const error = ref<MessageEvent | null>(null);
let channel: BroadcastChannel | null = null;
const post = (msg: T) => {
if (channel) {
channel.postMessage(msg);
}
};
const close = () => {
if (channel) {
channel.close();
channel = null;
}
};
onMounted(() => {
if (isSupported) {
channel = new BroadcastChannel(name);
channel.onmessage = (event) => {
data.value = event.data;
};
channel.onmessageerror = (event) => {
error.value = event;
console.error('BroadcastChannel Error:', event);
};
}
});
onUnmounted(() => {
close();
});
return {
isSupported,
data,
post,
close,
error
};
}三、 實戰範例:全域登出同步
這是 Broadcast Channel 最經典的應用場景。當使用者在任何一個分頁登出時,其他分頁應該自動跳轉回登入頁面。
3.1 登出控制元件 (LogoutButton.vue)
vue
<script setup lang="ts">
import { useBroadcastChannel } from '@/composables/useBroadcastChannel';
// 定義通訊格式
interface AuthMessage {
type: 'LOGIN' | 'LOGOUT';
payload?: any;
}
const { post } = useBroadcastChannel<AuthMessage>('auth_channel');
const handleLogout = () => {
// 1. 執行本地登出邏輯 (清除 Token 等)
localStorage.removeItem('token');
// 2. 通知其他分頁
post({ type: 'LOGOUT' });
// 3. 本頁跳轉
window.location.href = '/login';
};
</script>
<template>
<button @click="handleLogout">登出</button>
</template>3.2 應用程式根元件 (App.vue)
vue
<script setup lang="ts">
import { watch } from 'vue';
import { useBroadcastChannel } from '@/composables/useBroadcastChannel';
interface AuthMessage {
type: 'LOGIN' | 'LOGOUT';
}
const { data } = useBroadcastChannel<AuthMessage>('auth_channel');
// 監聽來自其他分頁的訊息
watch(data, (msg) => {
if (msg?.type === 'LOGOUT') {
alert('您已在其他分頁登出,即將返回登入頁面。');
window.location.href = '/login';
}
});
</script>四、 複雜度更低的狀態同步
除了強制登出,我們也可以用來同步「應用程式狀態」,例如:當使用者在列表頁新增一筆資料,詳情頁可以即時收到通知並重新整理。
typescript
// 列表頁
post({ type: 'REFRESH_LIST' });
// 詳情頁 / 其他列表頁
watch(data, (msg) => {
if (msg?.type === 'REFRESH_LIST') {
fetchData(); // 重新抓取 API
}
});總結
BroadcastChannel 是一個輕量且高效的 API,它極大地簡化了同源分頁間的通訊邏輯。透過 Vue Composable 的封裝,我們能以宣告式的方式處理跨視窗狀態,讓使用者體驗更加無縫與一致。
Web API 系列文章至此暫告一段落。從語音互動到硬體控制,再到系統深層整合,希望這 16 篇文章能幫助你挖掘瀏覽器的無限潛能!