Vue實(shí)現(xiàn)多窗口數(shù)據(jù)實(shí)時(shí)同步的三種常規(guī)方案詳解
要實(shí)現(xiàn)多窗口(或多標(biāo)簽頁)數(shù)據(jù)實(shí)時(shí)同步(無需刷新),核心是利用 瀏覽器跨窗口通信能力 結(jié)合 狀態(tài)管理,讓一個(gè)窗口的數(shù)據(jù)變化實(shí)時(shí)通知到其他窗口。以下是具體實(shí)現(xiàn)方案,按「通用性 + 復(fù)雜度」排序:
一、核心原理:瀏覽器跨窗口通信方案
瀏覽器提供了 3 種主流的跨窗口通信方式,均支持同源(同協(xié)議、同域名、同端口)下的窗口數(shù)據(jù)同步:
| 方案 | 核心 API | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場景 |
|---|---|---|---|---|
| Broadcast Channel | new BroadcastChannel() | 極簡(一行代碼發(fā)送 / 接收)、性能好 | 不支持 IE(移動(dòng)端 / 現(xiàn)代瀏覽器均支持) | 同源多窗口 / 多標(biāo)簽頁同步 |
| LocalStorage 監(jiān)聽 | window.addEventListener('storage') | 兼容性極強(qiáng)(支持所有瀏覽器) | 依賴存儲事件(需修改 localStorage) | 需兼容舊瀏覽器的場景 |
| SharedWorker | new SharedWorker() | 支持復(fù)雜邏輯(如實(shí)時(shí)計(jì)算、長連接) | 實(shí)現(xiàn)稍復(fù)雜(需單獨(dú)寫 Worker 文件) | 多窗口需共享復(fù)雜業(yè)務(wù)邏輯 |
二、具體實(shí)現(xiàn)方案(推薦優(yōu)先用 Broadcast Channel)
以「閱讀數(shù)量同步」為例,3 種方案的完整代碼如下,可直接復(fù)制使用:
方案 1:Broadcast Channel(最簡單,推薦)
原理:創(chuàng)建一個(gè)「廣播頻道」,所有窗口加入該頻道;當(dāng)一個(gè)窗口的閱讀數(shù)變化時(shí),向頻道發(fā)送消息,其他窗口接收消息后更新數(shù)據(jù)。
1. 封裝通信工具(可復(fù)用)
創(chuàng)建 src/utils/channel.js,統(tǒng)一管理廣播頻道:
// 1. 創(chuàng)建全局唯一的廣播頻道(頻道名自定義,需確保所有窗口一致)
const channel = new BroadcastChannel('read-count-sync');
// 2. 發(fā)送消息(數(shù)據(jù)變化時(shí)調(diào)用,如閱讀數(shù)+1)
export function sendReadCount(count) {
channel.postMessage({
type: 'READ_COUNT_UPDATE', // 消息類型(區(qū)分不同同步內(nèi)容)
data: count // 要同步的閱讀數(shù)
});
}
// 3. 接收消息(其他窗口發(fā)送消息時(shí)觸發(fā),用于更新本地?cái)?shù)據(jù))
export function onReadCountUpdate(callback) {
// 監(jiān)聽頻道消息
channel.onmessage = (e) => {
// 只處理閱讀數(shù)更新的消息
if (e.data.type === 'READ_COUNT_UPDATE') {
callback(e.data.data); // 把最新的閱讀數(shù)傳給回調(diào)函數(shù)
}
};
// 銷毀頻道(避免內(nèi)存泄漏,在組件卸載時(shí)調(diào)用)
return () => {
channel.close();
};
}
2. 窗口 A(閱讀操作窗口):發(fā)送數(shù)據(jù)變化
當(dāng)用戶在窗口 A 閱讀時(shí),更新本地閱讀數(shù),并通過廣播通知其他窗口:
<template>
<div class="read-page">
<h2>當(dāng)前閱讀數(shù):{{ readCount }}</h2>
<button @click="handleRead">點(diǎn)擊閱讀(+1)</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { sendReadCount } from '@/utils/channel';
// 本地閱讀數(shù)狀態(tài)(初始值可從接口/緩存獲?。?
const readCount = ref(0);
// 閱讀操作:更新本地?cái)?shù)據(jù) + 廣播通知其他窗口
const handleRead = () => {
readCount.value += 1;
// 發(fā)送最新的閱讀數(shù)到廣播頻道
sendReadCount(readCount.value);
};
</script>
3. 窗口 B(需同步的窗口):接收數(shù)據(jù)并更新
窗口 B 初始化時(shí)監(jiān)聽廣播,收到消息后更新本地彈框的閱讀數(shù):
<template>
<!-- 彈框組件:展示閱讀數(shù) -->
<van-dialog v-model:show="dialogShow" title="閱讀統(tǒng)計(jì)">
<p>當(dāng)前閱讀數(shù):{{ readCount }}</p>
</van-dialog>
</template>
<script setup>
import { ref, onUnmounted } from 'vue';
import { onReadCountUpdate } from '@/utils/channel';
// 彈框顯示狀態(tài)(假設(shè)默認(rèn)打開)
const dialogShow = ref(true);
// 本地閱讀數(shù)狀態(tài)(初始值可從接口/緩存獲?。?
const readCount = ref(0);
// 監(jiān)聽閱讀數(shù)更新:收到其他窗口的消息后,更新本地?cái)?shù)據(jù)
const destroyChannel = onReadCountUpdate((newCount) => {
readCount.value = newCount; // 無需刷新,實(shí)時(shí)更新彈框內(nèi)容
});
// 組件卸載時(shí)銷毀廣播頻道(避免內(nèi)存泄漏)
onUnmounted(() => {
destroyChannel();
});
</script>
方案 2:LocalStorage 監(jiān)聽(兼容所有瀏覽器)
原理:LocalStorage 是同源窗口共享的存儲;當(dāng)一個(gè)窗口修改 LocalStorage 時(shí),其他窗口會觸發(fā) storage 事件,通過監(jiān)聽該事件同步數(shù)據(jù)。
1. 封裝工具(src/utils/storageSync.js)
// 存儲的 key(自定義,需所有窗口一致)
const READ_COUNT_KEY = 'read-count';
// 1. 更新閱讀數(shù)并觸發(fā) storage 事件
export function updateReadCount(count) {
// 修改 LocalStorage(會觸發(fā)其他窗口的 storage 事件)
localStorage.setItem(READ_COUNT_KEY, JSON.stringify({
value: count,
timestamp: Date.now() // 避免舊數(shù)據(jù)覆蓋(可選)
}));
}
// 2. 監(jiān)聽閱讀數(shù)變化
export function onReadCountChange(callback) {
// 監(jiān)聽 storage 事件
const handleStorage = (e) => {
// 只處理閱讀數(shù)相關(guān)的存儲變化
if (e.key === READ_COUNT_KEY) {
const { value } = JSON.parse(e.newValue); // 最新的閱讀數(shù)
callback(value);
}
};
window.addEventListener('storage', handleStorage);
// 移除監(jiān)聽(組件卸載時(shí)調(diào)用)
return () => {
window.removeEventListener('storage', handleStorage);
};
}
// 3. 獲取當(dāng)前閱讀數(shù)(初始化時(shí)用)
export function getCurrentReadCount() {
const data = localStorage.getItem(READ_COUNT_KEY);
return data ? JSON.parse(data).value : 0;
}
2. 窗口 A(閱讀操作)
<script setup>
import { ref } from 'vue';
import { updateReadCount, getCurrentReadCount } from '@/utils/storageSync';
// 初始化:從 LocalStorage 獲取當(dāng)前閱讀數(shù)
const readCount = ref(getCurrentReadCount());
const handleRead = () => {
readCount.value += 1;
// 更新 LocalStorage,觸發(fā)其他窗口同步
updateReadCount(readCount.value);
};
</script>
3. 窗口 B(同步窗口)
<script setup>
import { ref, onUnmounted } from 'vue';
import { onReadCountChange, getCurrentReadCount } from '@/utils/storageSync';
const dialogShow = ref(true);
// 初始化:從 LocalStorage 獲取初始值
const readCount = ref(getCurrentReadCount());
// 監(jiān)聽 storage 事件,同步數(shù)據(jù)
const removeListener = onReadCountChange((newCount) => {
readCount.value = newCount; // 實(shí)時(shí)更新彈框
});
// 組件卸載時(shí)移除監(jiān)聽
onUnmounted(() => {
removeListener();
});
</script>
注意:storage 事件 只在其他窗口修改 LocalStorage 時(shí)觸發(fā),當(dāng)前窗口修改不會觸發(fā)(需手動(dòng)更新本地?cái)?shù)據(jù),如上代碼已處理)。
方案 3:SharedWorker(復(fù)雜場景用)
原理:創(chuàng)建一個(gè)共享的 Worker 線程,所有窗口通過 Worker 通信;Worker 作為「中間件」轉(zhuǎn)發(fā)數(shù)據(jù),支持更復(fù)雜的邏輯(如統(tǒng)計(jì)所有窗口的閱讀總數(shù))。
1. 創(chuàng)建 Worker 文件(src/workers/readSync.worker.js)
// Worker 線程:管理所有窗口的連接和消息轉(zhuǎn)發(fā)
const connections = new Set(); // 存儲所有窗口的連接
// 監(jiān)聽窗口連接
self.onconnect = (e) => {
const port = e.ports[0]; // 獲取當(dāng)前窗口的通信端口
connections.add(port); // 加入連接集合
// 監(jiān)聽窗口發(fā)送的消息
port.onmessage = (msg) => {
if (msg.data.type === 'READ_COUNT_UPDATE') {
// 轉(zhuǎn)發(fā)消息到所有其他窗口
connections.forEach(conn => {
if (conn !== port) { // 不發(fā)給自己
conn.postMessage(msg.data);
}
});
}
};
// 窗口斷開連接時(shí)移除
port.onmessageerror = () => {
connections.delete(port);
};
port.onerror = () => {
connections.delete(port);
};
};
2. 封裝工具(src/utils/workerSync.js)
// 1. 創(chuàng)建 SharedWorker 實(shí)例
const worker = new SharedWorker(new URL('@/workers/readSync.worker.js', import.meta.url));
const port = worker.port; // 獲取通信端口
port.start(); // 啟動(dòng)端口通信
// 2. 發(fā)送閱讀數(shù)更新
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);
}
};
// 銷毀連接(組件卸載時(shí)調(diào)用)
return () => {
port.close();
};
}
3. 窗口 A 和 B 的使用方式
與「方案 1(Broadcast Channel)」完全一致,只需替換導(dǎo)入的工具函數(shù)即可:
// 窗口 A 發(fā)送
import { sendReadCount } from '@/utils/workerSync';
// 窗口 B 接收
import { onReadCountUpdate } from '@/utils/workerSync';
三、注意
同源限制:以上 3 種方案均只支持 同源窗口(同協(xié)議、同域名、同端口);若跨域名(如 a.com 和 b.com),需用「跨域通信方案」(如 postMessage + iframe 橋接),但你的場景是同一項(xiàng)目的兩個(gè)窗口,無需考慮跨域。
初始值同步:新打開的窗口需先從「數(shù)據(jù)源」(如接口、LocalStorage)獲取初始閱讀數(shù),再監(jiān)聽后續(xù)變化;避免初始值為 0,導(dǎo)致同步前顯示錯(cuò)誤。
內(nèi)存泄漏:所有監(jiān)聽事件(如 onmessage、storage)需在組件卸載時(shí)銷毀(如關(guān)閉廣播頻道、移除 storage 監(jiān)聽、關(guān)閉 Worker 端口),避免瀏覽器內(nèi)存占用過高。
數(shù)據(jù)一致性:若多個(gè)窗口同時(shí)修改閱讀數(shù)(如窗口 A 和 B 同時(shí)點(diǎn)擊閱讀),需確保數(shù)據(jù)不會沖突;可通過「時(shí)間戳」或「版本號」判斷數(shù)據(jù)新舊,或讓 Worker 統(tǒng)一處理計(jì)數(shù)(方案 3 適合)。
到此這篇關(guān)于Vue實(shí)現(xiàn)多窗口數(shù)據(jù)實(shí)時(shí)同步的三種常規(guī)方案詳解的文章就介紹到這了,更多相關(guān)Vue數(shù)據(jù)實(shí)時(shí)同步內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- nodejs同步調(diào)用獲取mysql數(shù)據(jù)時(shí)遇到的大坑
- angularJs中ng-model-options設(shè)置數(shù)據(jù)同步的方法
- 淺談JSON的數(shù)據(jù)交換、緩存問題和同步問題
- node.js將MongoDB數(shù)據(jù)同步到MySQL的步驟
- Vue2父子組件數(shù)據(jù)傳遞與同步的方法詳解
- Vuex state中同步數(shù)據(jù)和異步數(shù)據(jù)方式
- vue3利用v-model實(shí)現(xiàn)父子組件之間數(shù)據(jù)同步的代碼詳解
- Vue3數(shù)據(jù)更新,頁面沒有同步更新的問題及解決
- vue3中使用v-model實(shí)現(xiàn)父子組件數(shù)據(jù)同步的三種方案
- 實(shí)現(xiàn)vuex與組件data之間的數(shù)據(jù)同步更新方式
相關(guān)文章
在Vue中使用SQLite數(shù)據(jù)庫的基礎(chǔ)應(yīng)用詳解
這篇文章主要為大家詳細(xì)介紹了在Vue中使用SQLite數(shù)據(jù)庫的基礎(chǔ)應(yīng)用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02
vue項(xiàng)目多租戶環(huán)境變量的設(shè)置
本文主要介紹了vue項(xiàng)目多租戶環(huán)境變量的設(shè)置,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Vue+Element UI實(shí)現(xiàn)下拉菜單的封裝
這篇文章主要為大家詳細(xì)介紹了Vue+Element UI實(shí)現(xiàn)下拉菜單的封裝代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Vue中video標(biāo)簽如何實(shí)現(xiàn)不靜音自動(dòng)播放
最近在做大屏展示需要在一開始播放引導(dǎo)視頻,產(chǎn)生自動(dòng)播放需求,下面這篇文章主要給大家介紹了關(guān)于Vue中video標(biāo)簽如何實(shí)現(xiàn)不靜音自動(dòng)播放的相關(guān)資料,需要的朋友可以參考下2023-01-01

