跨瀏覽器窗口通信,竟然有 7 種方式
前言
爲什麼會扯到這個話題,最初是源於聽 https://y.qq.com/ QQ 音樂,
-
播放器處於單獨的一個頁面
-
當你在另外的一個頁面搜索到你滿意的歌曲的時候,點擊播放或添加到播放隊列
-
你會發現,播放器頁面做出了響應的響應
這裏我又聯想到了商城的購物車的場景,體驗確實有提升。
剛開始,我懷疑的是 Web Socket 作妖,結果通過分析網絡請求和看源碼,並沒有。最後發現是 localStore 的 storage 事件作妖,哈哈。
迴歸正題,其實在一般正常的知識儲備的情況下,我們會想到哪些方案呢?
先拋開如下方式:
-
各自對服務器進行輪詢或者長輪詢
-
同源策略下,一方是另一方的
opener
演示和源碼
多頁面通訊的 demo, 爲了正常運行,請用最新的 chrome 瀏覽器打開。
demo 的源碼地址 [1]
-
首頁 [2]
-
setInterval + sessionStorage[3]
-
localStorage[4]
-
BroadcastChannel[5]
-
SharedWorker[6]
兩個瀏覽器窗口間通信
WebSocket
這個沒有太多解釋,WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。當然是有代價的,需要服務器來支持。
js 語言,現在比較成熟穩定當然是 socket.io[7] 和 ws[8]. 也還有輕量級的 ClusterWS[9]。
你可以在 The WebSocket API (WebSockets) 看到更多的關於 Web Socket 的信息。
定時器 + 客戶端存儲
定時器:setTimeout/setInterval/requestAnimationFrame
客戶端存儲:cookie/localStorage/sessionStorage/indexDB/chrome 的 FileSystem
定時器沒啥好說的,關於客戶端存儲。
-
cookie: 每次會帶到服務端,並且能存的並不大,4kb?,記得不太清楚
-
localStorage/sessionStorage 應該是 5MB, sessionStorage 關閉瀏覽器就和你說拜拜。
-
indexDB 這玩意就強大了,不過讀取都是異步的,還能存 Blob 文件,真的是很 high。
-
chrome 的 FileSystem ,Filesystem & FileWriter API[10], 主要是 chrome 和 opera 支持。這玩意就是文件系統。
postMessage
Cross-document messaging[11] 這玩意的支持率 98.9%。好像還能發送文件,哈哈,強大。
不過仔細一看 window.postMessage(),就註定了你首先得拿到 window 這個對象。也註定他使用的限制, 兩個窗體必須建立起聯繫。常見建立聯繫的方式:
-
window.open
-
window.opener
-
iframe
提到上面的 window.open, open 後你能獲得被打開窗體的句柄,當然也可以直接操作窗體了。
到這裏,我覺得一般的前端人員能想到的比較正經的方案應該是上面三種啦。
當然,我們接下來說說可能不是那麼常見的另外三種方式。
StorageEvent
Page 1
localStorage.setItem('message',JSON.stringify({
message: '消息',
from: 'Page 1',
date: Date.now()
}))
Page 2
window.addEventListener("storage", function(e) {
console.log(e.key, e.newValue, e.oldValue)
});
如上, Page 1 設置消息, Page 2 註冊 storage 事件,就能監聽到數據的變化啦。
上面的 e 就是 StorageEvent[12], 有下面特有的屬性(都是隻讀):
-
key :代表屬性名發生變化. 當被 clear() 方法清除之後所有屬性名變爲 null
-
newValue:新添加進的值. 當被 clear() 方法執行過或者鍵名已被刪除時值爲 null
-
oldValue:原始值. 而被 clear() 方法執行過,或在設置新值之前並沒有設置初始值時則返回 null
-
storageArea:被操作的 storage 對象
-
url:key 發生改變的對象所在文檔的 URL 地址
Broadcast Channel
這玩意主要就是給多窗口用的,Service Woker 也可以使用。firefox,chrome, Opera 均支持,有時候真的是很討厭 Safari,瀏覽器支持 77% 左右。
使用起來也很簡單, 創建 BroadcastChannel, 然後監聽事件。只需要注意一點,渠道名稱一致就可以。
Page 1
var channel = new BroadcastChannel("channel-BroadcastChannel");
channel.postMessage('Hello, BroadcastChannel!')
Page 2
var channel = new BroadcastChannel("channel-BroadcastChannel");
channel.addEventListener("message", function(ev) {
console.log(ev.data)
});
SharedWorker
這是 Web Worker 之後出來的共享的 Worker,不同頁面可以共享這個 Worker。
MDN 這裏給了一個比較完整的例子 simple-shared-worker[13]。
這裏來個插曲,Safari 有幾個版本支持這個特性,後來又不支持啦,還是你 Safari,真是 6。
雖然,SharedWorker 本身的資源是共享的,但是要想達到多頁面的互相通訊,那還是要做一些手腳的。先看看 MDN 給出的例子的 ShareWoker 本身的代碼:
onconnect = function(e) {
var port = e.ports[0];
port.onmessage = function(e) {
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
port.postMessage(workerResult);
}
}
上面的代碼其實很簡單,port 是關鍵,這個 port 就是和各個頁面通訊的主宰者,既然 SharedWorker 資源是共享的,那好辦,把 port 存起來就是啦。
看一下,如下改造的代碼:
SharedWorker 就成爲一個純粹的訂閱發佈者啦,哈哈。
var portList = [];
onconnect = function(e) {
var port = e.ports[0];
ensurePorts(port);
port.onmessage = function(e) {
var data = e.data;
disptach(port, data);
};
port.start();
};
function ensurePorts(port) {
if (portList.indexOf(port) < 0) {
portList.push(port);
}
}
function disptach(selfPort, data) {
portList
.filter(port => selfPort !== port)
.forEach(port => port.postMessage(data));
}
MessageChannel[14]
Channel Messaging API 的 MessageChannel
接口允許我們創建一個新的消息通道,並通過它的兩個 MessagePort
[15] 屬性發送數據。
其需要先通過 postMessage 先建立聯繫。
MessageChannel 的基本使用:
var channel = new MessageChannel();
var para = document.querySelector('p');
var ifr = document.querySelector('iframe');
var otherWindow = ifr.contentWindow;
ifr.addEventListener("load", iframeLoaded, false);
function iframeLoaded() {
otherWindow.postMessage('Hello from the main page!', '*', [channel.port2]);
}
channel.port1.onmessage = handleMessage;
function handleMessage(e) {
para.innerHTML = e.data;
}
至於在線的例子,MDN 官方有一個版本 MessageChannel 通訊 [16]
引用
MDN Web Docs - Broadcast Channel[17]
BroadcastChannel | Can I Use[18]
broadcast-channel[19]
StorageEvent[20]
SharedWorker[21]
simple-shared-worker[22]
SharedWorker | Can I Use[23]
共享線程 SharedWorker[24]
feature-shared-web-workers[25] 兩個瀏覽器窗口間通信總結 [26]
參考資料
[1] demo 的源碼地址: https://github.com/xiangwenhu/page-communication/tree/master/docs
[2] 首頁: https://xiangwenhu.github.io/page-communication/
[3] setInterval + sessionStorage: https://xiangwenhu.github.io/page-communication/setInterval/index.html
[4] localStorage: https://xiangwenhu.github.io/page-communication/localStorage/index.html
[5] BroadcastChannel: https://xiangwenhu.github.io/page-communication/BroadcastChannel/index.html
[6] SharedWorker: https://xiangwenhu.github.io/page-communication/SharedWorker/index.html
[7] socket.io: https://github.com/socketio/socket.io
[8] ws: https://github.com/websockets/ws
[9] ClusterWS: https://github.com/ClusterWS/ClusterWS
[10] Filesystem & FileWriter API: https://caniuse.com/#search=fileSystem
[11] Cross-document messaging: https://caniuse.com/#search=postMessage
[12] StorageEvent: https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent
[13] simple-shared-worker: https://github.com/mdn/simple-shared-worker
[14] MessageChannel: https://developer.mozilla.org/zh-CN/docs/Web/API/MessageChannel
[15] MessagePort
: https://developer.mozilla.org/zh-CN/docs/Web/API/MessagePort
[16] MessageChannel 通訊: https://mdn.github.io/dom-examples/channel-messaging-basic/
[17] MDN Web Docs - Broadcast Channel: https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel
[18] BroadcastChannel | Can I Use: https://caniuse.com/#search=BroadcastChannel
[19] broadcast-channel: https://github.com/pubkey/broadcast-channel
[20] StorageEvent: https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent
[21] SharedWorker: https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker
[22] simple-shared-worker: https://github.com/mdn/simple-shared-worker/blob/gh-pages/worker.js
[23] SharedWorker | Can I Use: https://caniuse.com/#search=SharedWorker
[24] 共享線程 SharedWorker: https://blog.csdn.net/qq_38177681/article/details/82048895
[25] feature-shared-web-workers: https://webkit.org/status/#feature-shared-web-workers
[26] 兩個瀏覽器窗口間通信總結: https://segmentfault.com/a/1190000016927268
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/q9trh3-RjeMAO7rAUc2qJQ