百度视频播放器安卓版(百搜视频)
171.93MB · 2025-11-02
要实现多窗口(或多标签页)数据实时同步(无需刷新),核心是利用 浏览器跨窗口通信能力 结合 状态管理,让一个窗口的数据变化实时通知到其他窗口。以下是具体实现方案,按「通用性 + 复杂度」排序:
浏览器提供了 3 种主流的跨窗口通信方式,均支持同源(同协议、同域名、同端口)下的窗口数据同步:
| 方案 | 核心 API | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Broadcast Channel | new BroadcastChannel() | 极简(一行代码发送 / 接收)、性能好 | 不支持 IE(移动端 / 现代浏览器均支持) | 同源多窗口 / 多标签页同步 |
| LocalStorage 监听 | window.addEventListener('storage') | 兼容性极强(支持所有浏览器) | 依赖存储事件(需修改 localStorage) | 需兼容旧浏览器的场景 |
| SharedWorker | new SharedWorker() | 支持复杂逻辑(如实时计算、长连接) | 实现稍复杂(需单独写 Worker 文件) | 多窗口需共享复杂业务逻辑 |
以「阅读数量同步」为例,3 种方案的完整代码如下,可直接复制使用:
原理:创建一个「广播频道」,所有窗口加入该频道;当一个窗口的阅读数变化时,向频道发送消息,其他窗口接收消息后更新数据。
创建 src/utils/channel.js,统一管理广播频道:
// 1. 创建全局唯一的广播频道(频道名自定义,需确保所有窗口一致)
const channel = new BroadcastChannel('read-count-sync');
// 2. 发送消息(数据变化时调用,如阅读数+1)
export function sendReadCount(count) {
channel.postMessage({
type: 'READ_COUNT_UPDATE', // 消息类型(区分不同同步内容)
data: count // 要同步的阅读数
});
}
// 3. 接收消息(其他窗口发送消息时触发,用于更新本地数据)
export function onReadCountUpdate(callback) {
// 监听频道消息
channel.onmessage = (e) => {
// 只处理阅读数更新的消息
if (e.data.type === 'READ_COUNT_UPDATE') {
callback(e.data.data); // 把最新的阅读数传给回调函数
}
};
// 销毁频道(避免内存泄漏,在组件卸载时调用)
return () => {
channel.close();
};
}
当用户在窗口 A 阅读时,更新本地阅读数,并通过广播通知其他窗口:
<template>
<div class="read-page">
<h2>当前阅读数:{{ readCount }}</h2>
<button @click="handleRead">点击阅读(+1)</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { sendReadCount } from '@/utils/channel';
// 本地阅读数状态(初始值可从接口/缓存获取)
const readCount = ref(0);
// 阅读操作:更新本地数据 + 广播通知其他窗口
const handleRead = () => {
readCount.value += 1;
// 发送最新的阅读数到广播频道
sendReadCount(readCount.value);
};
</script>
窗口 B 初始化时监听广播,收到消息后更新本地弹框的阅读数:
<template>
<!-- 弹框组件:展示阅读数 -->
<van-dialog v-model:show="dialogShow" title="阅读统计">
<p>当前阅读数:{{ readCount }}</p>
</van-dialog>
</template>
<script setup>
import { ref, onUnmounted } from 'vue';
import { onReadCountUpdate } from '@/utils/channel';
// 弹框显示状态(假设默认打开)
const dialogShow = ref(true);
// 本地阅读数状态(初始值可从接口/缓存获取)
const readCount = ref(0);
// 监听阅读数更新:收到其他窗口的消息后,更新本地数据
const destroyChannel = onReadCountUpdate((newCount) => {
readCount.value = newCount; // 无需刷新,实时更新弹框内容
});
// 组件卸载时销毁广播频道(避免内存泄漏)
onUnmounted(() => {
destroyChannel();
});
</script>
原理:LocalStorage 是同源窗口共享的存储;当一个窗口修改 LocalStorage 时,其他窗口会触发 storage 事件,通过监听该事件同步数据。
src/utils/storageSync.js)// 存储的 key(自定义,需所有窗口一致)
const READ_COUNT_KEY = 'read-count';
// 1. 更新阅读数并触发 storage 事件
export function updateReadCount(count) {
// 修改 LocalStorage(会触发其他窗口的 storage 事件)
localStorage.setItem(READ_COUNT_KEY, JSON.stringify({
value: count,
timestamp: Date.now() // 避免旧数据覆盖(可选)
}));
}
// 2. 监听阅读数变化
export function onReadCountChange(callback) {
// 监听 storage 事件
const handleStorage = (e) => {
// 只处理阅读数相关的存储变化
if (e.key === READ_COUNT_KEY) {
const { value } = JSON.parse(e.newValue); // 最新的阅读数
callback(value);
}
};
window.addEventListener('storage', handleStorage);
// 移除监听(组件卸载时调用)
return () => {
window.removeEventListener('storage', handleStorage);
};
}
// 3. 获取当前阅读数(初始化时用)
export function getCurrentReadCount() {
const data = localStorage.getItem(READ_COUNT_KEY);
return data ? JSON.parse(data).value : 0;
}
<script setup>
import { ref } from 'vue';
import { updateReadCount, getCurrentReadCount } from '@/utils/storageSync';
// 初始化:从 LocalStorage 获取当前阅读数
const readCount = ref(getCurrentReadCount());
const handleRead = () => {
readCount.value += 1;
// 更新 LocalStorage,触发其他窗口同步
updateReadCount(readCount.value);
};
</script>
<script setup>
import { ref, onUnmounted } from 'vue';
import { onReadCountChange, getCurrentReadCount } from '@/utils/storageSync';
const dialogShow = ref(true);
// 初始化:从 LocalStorage 获取初始值
const readCount = ref(getCurrentReadCount());
// 监听 storage 事件,同步数据
const removeListener = onReadCountChange((newCount) => {
readCount.value = newCount; // 实时更新弹框
});
// 组件卸载时移除监听
onUnmounted(() => {
removeListener();
});
</script>
注意:storage 事件 只在其他窗口修改 LocalStorage 时触发,当前窗口修改不会触发(需手动更新本地数据,如上代码已处理)。
原理:创建一个共享的 Worker 线程,所有窗口通过 Worker 通信;Worker 作为「中间件」转发数据,支持更复杂的逻辑(如统计所有窗口的阅读总数)。
src/workers/readSync.worker.js)// Worker 线程:管理所有窗口的连接和消息转发
const connections = new Set(); // 存储所有窗口的连接
// 监听窗口连接
self.onconnect = (e) => {
const port = e.ports[0]; // 获取当前窗口的通信端口
connections.add(port); // 加入连接集合
// 监听窗口发送的消息
port.onmessage = (msg) => {
if (msg.data.type === 'READ_COUNT_UPDATE') {
// 转发消息到所有其他窗口
connections.forEach(conn => {
if (conn !== port) { // 不发给自己
conn.postMessage(msg.data);
}
});
}
};
// 窗口断开连接时移除
port.onmessageerror = () => {
connections.delete(port);
};
port.onerror = () => {
connections.delete(port);
};
};
src/utils/workerSync.js)// 1. 创建 SharedWorker 实例
const worker = new SharedWorker(new URL('@/workers/readSync.worker.js', import.meta.url));
const port = worker.port; // 获取通信端口
port.start(); // 启动端口通信
// 2. 发送阅读数更新
export function sendReadCount(count) {
port.postMessage({
type: 'READ_COUNT_UPDATE',
data: count
});
}
// 3. 接收其他窗口的更新
export function onReadCountUpdate(callback) {
port.onmessage = (e) => {
if (e.data.type === 'READ_COUNT_UPDATE') {
callback(e.data.data);
}
};
// 销毁连接(组件卸载时调用)
return () => {
port.close();
};
}
与「方案 1(Broadcast Channel)」完全一致,只需替换导入的工具函数即可:
// 窗口 A 发送
import { sendReadCount } from '@/utils/workerSync';
// 窗口 B 接收
import { onReadCountUpdate } from '@/utils/workerSync';
a.com 和 b.com),需用「跨域通信方案」(如 postMessage + iframe 桥接),但你的场景是同一项目的两个窗口,无需考虑跨域。onmessage、storage)需在组件卸载时销毁(如关闭广播频道、移除 storage 监听、关闭 Worker 端口),避免浏览器内存占用过高。