需求描述
vue 項目中有一個工單消息通知清單頁,每條消息有已讀和未讀狀态,點選消息會用 window.open 打開一個新的浏覽器标簽頁跳轉到工單清單頁,工單清單頁裡有項操作是檢視消息,會彈窗顯示出具體的詳細内容,進入這個彈窗就代表使用者已經看到消息了,此時會去調後端接口修改消息狀态為已讀。
需要實作使用者已讀目前消息後,前面一個工單消息通知清單頁的已讀/未讀狀态同步更新。
想到的實作方式
- 1、直接用定時器 setInterval 不斷地輪詢調後端接口查最新狀态
- 2、用 vuex 實作全局狀态共享
- 3、用 localStorage 實作資料共享
第1種最簡單,不過有點 low 直接 pass 掉了。第2種乍一看好像可以實作,實際上你去試一下就會發現問題,vuex 是不能跨标簽頁共享的,每一個标簽頁下的 vuex 執行個體是獨立的,你修改了目前标簽頁下的 vuex 裡資料,其他标簽頁是不受影響的,網上找到一個插件 vuex-shared-mutations,不過看了下最近維護還是4年以前,是以也直接 pass 了。
最後用第3種實作了,自己在 localStorage 裡存一個辨別,然後在需要同步資料的頁面開一個 storage 的事件監聽,去監聽本地緩存的變化,如果是目标值發生變化,直接重新擷取資料更新就可以了,具體的 demo 代碼:
消息通知清單頁
<template>
<div>
<p v-for="(item, index) in noticeList" :key="index">消息{{ index + 1 }}:{{ item.title }}</p>
</div>
</template>
<script>
export default {
data() {
return {
noticeList: [
{ title: '我是第一條消息' },
{ title: '我是第二條消息' },
]
}
},
created() {
// 跨标簽頁監聽
window.addEventListener('storage', this.storageChange)
},
beforeDestroy() {
window.removeEventListener('storage', this.storageChange)
},
methods: {
storageChange(e) {
if (e && e.key == 'targetKey' && e.newValue) {
// 在這裡更新資料
}
},
}
}
</script>
工單清單頁
<template>
<div>
<p @click="handleRead">讀消息</p>
</div>
</template>
<script>
export default {
data() {
return {}
},
methods: {
handleRead() {
localStorage.setItem('targetKey', 1)
},
}
}
</script>
效果是實作了,最終感覺還是不太滿意,在群裡詢問了一圈,果然有新發現,postMessage 和 BroadcastChannel 這兩個浏覽器的新 api 是可以實作跨标簽頁通信的,postMessage 還可以實作跨源通信,一般用在和 iframe 通信比較多。
終極版
最終嘗試了 BroadcastChannel 版本:
接收消息:
const BC = new BroadcastChannel('notice')
BC.onmessage = (e) => {
console.log('消息來了:', e)
}
// 斷開連接配接
// BC.close()
發送消息:
const BC = new BroadcastChannel('notice')
BC.postMessage('發出消息,趕緊去更新資料吧')
// 斷開連接配接
// BC.close()
相關文檔
- BroadcastChannel
- postMessage