問題
兩個窗口,顯示組件,A窗口顯示A組件,B窗口顯示B組件,兩個組件共同訂閲一個Service中的Subject,當在A組件中進行修改後,Service中的Subject發送通知,“值變更了”,B組件訂閲不到。
現在打開兩個窗口,每個窗口顯示一個組件,每個組件都訂閲 Service 中的 subjectTest, 在AComponent組件中,每隔一秒發送一條數據:
AComponent 組件中
let i = 0;
interval(1000).subscribe(() => {
this.systemService.abc(++i);
})
this.systemService.subjectTest.subscribe(v => {
console.log("AComponent收到了通知", v);
});
Service:
subjectTest = new Subject<any>();
abc(s :any) {
console.log('發送通知', s)
this.subjectTest.next(s)
}
BComponent組件中也訂閲:
this.systemService.subjectTest.subscribe(v => {
console.log('B Component 組件收到了通知',v);
})
效果如下:
藍色窗口:AComponet
紅色窗口:BComponent
可以看AComponent一值在調用abc()方法,發送通知,AComponent組件收到了通知,BComponent組件並沒有。
問題分析
我們當前 編寫的都是 ts 代碼,ts不能在 瀏覽器中直接運行,需要編譯 成js文件,在瀏覽器中運行。
JS 內存模型
js的內存主要分為兩個部分:棧 和 堆
棧 (Stack)
後進先出原則
🌰: 當調用一個函數時,函數的局部變量、參數和返回地址會被壓入棧,函數調用結束後,數據會從棧中彈出。
堆 (Heap)
存儲複雜和動態分配的對象
🌰: 聲明一個全局變量,引入service、定義Subject,都存儲在堆中。
瀏覽器新建一個窗口
每個窗口都是獨立的 棧 和 堆 :
在:藍色窗口中定義一個a變量,進行打印,沒問題,在紅色窗口中輸出輸出 a 是一個 為定義的變量。
所以當在AComponet 中訂閲了subjectTest,調用了 abc(), 發送了推送,AComponet組件也接受到了,而BComponent 雖然訂閲了subject 卻收不到通知,是因為兩個 sujectTest 是兩個獨立的可訂閲變量。
解決
利用瀏覽器的 localStorage 事件
在AComponet 組件中加入:localStorage事件
因為需要在AComponet 組件中發送 通知,那麼當AComponet 收到通知後,發送localStorage 事件
在BComponent 組件中監聽storage 事件,在判斷是否是響應 鍵。
window.addEventListener('storage', (event) => {
if (event.key === 'push') {
console.log('ndexComponent 組件收到了通知', event.newValue);
}
});
只有 同源 頁面(相同的協議、域名和端口)才會響應 storage 事件。
在同一個標籤頁中對 localStorage 的修改不會觸發 storage 事件。
藍色窗口:AComponet
紅色窗口:BComponent
使用 Broadcast Channel
一個所有同源頁面都可以共享的(廣播)頻道,因此其中某一個頁面發送的消息可以被其他頁面監聽到。
在A組件中繼續每隔 3s 發佈一次
let i = 0;
interval(1000 * 3).subscribe(() => {
this.systemService.abc(++i);
});
service 中定義 BroadcastChannel,創建通道名稱為“push”,只有頻道相同才互通。
channel = new BroadcastChannel('push');
abc(s :any) {
console.log('發送通知', s)
this.channel.postMessage(s);
}
B組件:
this.systemService.channel.onmessage = (event) => {
console.log('BComponent 組件收到了通知',event.data);
}
效果:
藍色窗口:AComponet
紅色窗口:BComponent
總結:
兩者都可以跨窗口同步信息, 兩者最主要的區別的在於,數據持久化。
localStorage的數據保存在瀏覽器中,刷新頁面,數據依舊存在。
BroadcastChannel的數據在會話中。刷新頁面,之前的數據就沒有了,得重新開始。
相對使用環境:使用BroadcastChannel更適合我們,刷新後,我們重新請求後台,已是最新數據,而且更改輪詢時間等,都是看着不對眼,才會設置。