瀏覽器多個(gè)標(biāo)簽頁之間的通信

一:websocket通訊

全雙工(full-duplex)通信自然可以實(shí)現(xiàn)多個(gè)標(biāo)簽頁之間的通信

WebSocket是HTML5新增的協(xié)議,它的目的是在瀏覽器和服務(wù)器之間建立一個(gè)不受限的雙向通信的通道,比如說,服務(wù)器可以在任意時(shí)刻發(fā)送消息給瀏覽器。為什么傳統(tǒng)的HTTP協(xié)議不能做到WebSocket實(shí)現(xiàn)的功能?這是因?yàn)镠TTP協(xié)議是一個(gè)請求-響應(yīng)協(xié)議,請求必須先由瀏覽器發(fā)給服務(wù)器,服務(wù)器才能響應(yīng)這個(gè)請求,再把數(shù)據(jù)發(fā)送給瀏覽器。

也有人說,HTTP協(xié)議其實(shí)也能實(shí)現(xiàn)啊,比如用輪詢或者Comet。這個(gè)機(jī)制的缺點(diǎn)一是實(shí)時(shí)性不夠,二是頻繁的請求會給服務(wù)器帶來極大的壓力。

Comet本質(zhì)上也是輪詢,但是在沒有消息的情況下,服務(wù)器先拖一段時(shí)間,等到有消息了再回復(fù)。這個(gè)機(jī)制暫時(shí)地解決了實(shí)時(shí)性問題,但是它帶來了新的問題:以多線程模式運(yùn)行的服務(wù)器會讓大部分線程大部分時(shí)間都處于掛起狀態(tài),極大地浪費(fèi)服務(wù)器資源。另外,一個(gè)HTTP連接在長時(shí)間沒有數(shù)據(jù)傳輸?shù)那闆r下,鏈路上的任何一個(gè)網(wǎng)關(guān)都可能關(guān)閉這個(gè)連接,而網(wǎng)關(guān)是我們不可控的,這就要求Comet連接必須定期發(fā)一些ping數(shù)據(jù)表示連接“正常工作”。

WebSocket并不是全新的協(xié)議,而是利用了HTTP協(xié)議來建立連接。為什么WebSocket連接可以實(shí)現(xiàn)全雙工通信而HTTP連接不行呢?實(shí)際上HTTP協(xié)議是建立在TCP協(xié)議之上的,TCP協(xié)議本身就實(shí)現(xiàn)了全雙工通信,但是HTTP協(xié)議的請求-應(yīng)答機(jī)制限制了全雙工通信。WebSocket連接建立以后,其實(shí)只是簡單規(guī)定了一下:接下來,咱們通信就不使用HTTP協(xié)議了,直接互相發(fā)數(shù)據(jù)吧。安全的WebSocket連接機(jī)制和HTTPS類似。首先,瀏覽器用wss://xxx創(chuàng)建WebSocket連接時(shí),會先通過HTTPS創(chuàng)建安全的連接,然后,該HTTPS連接升級為WebSocket連接,底層通信走的仍然是安全的SSL/TLS協(xié)議。

WebSocket連接必須由瀏覽器發(fā)起,特點(diǎn):

(1)建立在 TCP 協(xié)議之上,服務(wù)器端的實(shí)現(xiàn)比較容易。

(2)與 HTTP 協(xié)議有著良好的兼容性。默認(rèn)端口也是80和443,并且握手階段采用 HTTP 協(xié)議,因此握手時(shí)不容易屏蔽,能通過各種 HTTP 代理服務(wù)器。

(3)數(shù)據(jù)格式比較輕量,性能開銷小,通信高效。

(4)可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)。

(5)沒有同源限制,客戶端可以與任意服務(wù)器通信。

(6)協(xié)議標(biāo)識符是ws(如果加密,則為wss),服務(wù)器網(wǎng)址就是 URL。

二:定時(shí)器setInterval+cookie

在頁面A設(shè)置一個(gè)使用?setInterval?定時(shí)器不斷刷新,檢查?Cookies?的值是否發(fā)生變化,如果變化就進(jìn)行刷新的操作。

由于?Cookies?是在同域可讀的,所以在頁面 B 審核的時(shí)候改變?Cookies?的值,頁面 A 自然是可以拿到的。

這樣做確實(shí)可以實(shí)現(xiàn)我想要的功能,但是這樣的方法相當(dāng)浪費(fèi)資源。雖然在這個(gè)性能過盛的時(shí)代,浪費(fèi)不浪費(fèi)也感覺不出來,但是這種實(shí)現(xiàn)方案,確實(shí)不夠優(yōu)雅。

三:使用localstorage

localstorage是瀏覽器多個(gè)標(biāo)簽共用的存儲空間,所以可以用來實(shí)現(xiàn)多標(biāo)簽之間的通信(ps:session是會話級的存儲空間,每個(gè)標(biāo)簽頁都是單獨(dú)的)。

直接在window對象上添加監(jiān)聽即可:

window.onstorage = (e) => {console.log(e)}// 或者這樣window.addEventListener('storage', (e) => console.log(e))

onstorage以及storage事件,針對都是非當(dāng)前頁面對localStorage進(jìn)行修改時(shí)才會觸發(fā),當(dāng)前頁面修改localStorage不會觸發(fā)監(jiān)聽函數(shù)。然后就是在對原有的數(shù)據(jù)的值進(jìn)行修改時(shí)才會觸發(fā),比如原本已經(jīng)有一個(gè)key會a值為b的localStorage,你再執(zhí)行:localStorage.setItem('a', 'b')代碼,同樣是不會觸發(fā)監(jiān)聽函數(shù)的。

四:html5瀏覽器的新特性SharedWorker

普通的webworker直接使用new Worker()即可創(chuàng)建,這種webworker是當(dāng)前頁面專有的。然后還有種共享worker(SharedWorker),這種是可以多個(gè)標(biāo)簽頁、iframe共同使用的。

SharedWorker可以被多個(gè)window共同使用,但必須保證這些標(biāo)簽頁都是同源的(相同的協(xié)議,主機(jī)和端口號)

首先新建一個(gè)js文件worker.js,具體代碼如下:

// sharedWorker所要用到的js文件,不必打包到項(xiàng)目中,直接放到服務(wù)器即可let data = ''onconnect = function (e) {? let port = e.ports[0]? port.onmessage = function (e) {? ? if (e.data === 'get') {? ? ? port.postMessage(data)? ? } else {? ? ? data = e.data? ? }? }}

webworker端(暫且這樣稱呼)的代碼就如上,只需注冊一個(gè)onmessage監(jiān)聽信息的事件,客戶端(即使用sharedWorker的標(biāo)簽頁)發(fā)送message時(shí)就會觸發(fā)。

注意webworker無法在本地使用,出于瀏覽器本身的安全機(jī)制,所以我這次的示例也是放在服務(wù)器上的,worker.js和index.html在同一目錄。

因?yàn)榭蛻舳撕蛍ebworker端的通信不像websocket那樣是全雙工的,所以客戶端發(fā)送數(shù)據(jù)和接收數(shù)據(jù)要分成兩步來處理。示例中會有兩個(gè)按鈕,分別對應(yīng)的向sharedWorker發(fā)送數(shù)據(jù)的請求以及獲取數(shù)據(jù)的請求,但他們本質(zhì)上都是相同的事件--發(fā)送消息。

webworker端會進(jìn)行判斷,傳遞的數(shù)據(jù)為'get'時(shí),就把變量data的值回傳給客戶端,其他情況,則把客戶端傳遞過來的數(shù)據(jù)存儲到data變量中。下面是客戶端的代碼:

// 這段代碼是必須的,打開頁面后注冊SharedWorker,顯示指定worker.port.start()方法建立與worker間的連接

? ? if (typeof Worker === "undefined") {

? ? ? alert('當(dāng)前瀏覽器不支持webworker')

? ? } else {

? ? ? let worker = new SharedWorker('worker.js')

? ? ? worker.port.addEventListener('message', (e) => {

? ? ? ? console.log('來自worker的數(shù)據(jù):', e.data)

? ? ? }, false)

? ? ? worker.port.start()

? ? ? window.worker = worker

? ? }

// 獲取和發(fā)送消息都是調(diào)用postMessage方法,我這里約定的是傳遞'get'表示獲取數(shù)據(jù)。

window.worker.port.postMessage('get')

window.worker.port.postMessage('發(fā)送信息給worker')

頁面A發(fā)送數(shù)據(jù)給worker,然后打開頁面B,調(diào)用window.worker.port.postMessage('get'),即可收到頁面A發(fā)送給worker的數(shù)據(jù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容