兩個瀏覽器窗口間通信
WebSocket
這個沒有太多解釋,WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。當然是有代價的,需要服務器來支持。
js語言,現在比較成熟穩定當然是 socket.io和ws. 也還有輕量級的ClusterWS。
你可以在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,主要是chrome和opera支持。這玩意就是文件系統。
postMessage
Cross-document messaging 這玩意的支持率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,有下面特有的屬性(都是只讀):
- 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。
這里來個插曲,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
Channel Messaging API的 MessageChannel
接口允許我們創建一個新的消息通道,並通過它的兩個MessagePort
屬性發送數據。
其需要先通過 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 通訊