一:websocket通訊
全雙工(full-duplex)通信自然可以實(shí)現(xiàn)多個標(biāo)簽頁之間的通信
WebSocket是HTML5新增的協(xié)議哥桥,它的目的是在瀏覽器和服務(wù)器之間建立一個不受限的雙向通信的通道,比如說壮不,服務(wù)器可以在任意時刻發(fā)送消息給瀏覽器由缆。為什么傳統(tǒng)的HTTP協(xié)議不能做到WebSocket實(shí)現(xiàn)的功能注祖?這是因?yàn)镠TTP協(xié)議是一個請求-響應(yīng)協(xié)議,請求必須先由瀏覽器發(fā)給服務(wù)器均唉,服務(wù)器才能響應(yīng)這個請求是晨,再把數(shù)據(jù)發(fā)送給瀏覽器。
也有人說舔箭,HTTP協(xié)議其實(shí)也能實(shí)現(xiàn)啊罩缴,比如用輪詢或者Comet。這個機(jī)制的缺點(diǎn)一是實(shí)時性不夠层扶,二是頻繁的請求會給服務(wù)器帶來極大的壓力箫章。
Comet本質(zhì)上也是輪詢,但是在沒有消息的情況下怒医,服務(wù)器先拖一段時間炉抒,等到有消息了再回復(fù)。這個機(jī)制暫時地解決了實(shí)時性問題稚叹,但是它帶來了新的問題:以多線程模式運(yùn)行的服務(wù)器會讓大部分線程大部分時間都處于掛起狀態(tài)焰薄,極大地浪費(fèi)服務(wù)器資源。另外扒袖,一個HTTP連接在長時間沒有數(shù)據(jù)傳輸?shù)那闆r下塞茅,鏈路上的任何一個網(wǎng)關(guān)都可能關(guān)閉這個連接,而網(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連接時生巡,會先通過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é)議寥院,因此握手時不容易屏蔽劲赠,能通過各種 HTTP 代理服務(wù)器。
(3)數(shù)據(jù)格式比較輕量秸谢,性能開銷小凛澎,通信高效。
(4)可以發(fā)送文本估蹄,也可以發(fā)送二進(jìn)制數(shù)據(jù)塑煎。
(5)沒有同源限制,客戶端可以與任意服務(wù)器通信臭蚁。
(6)協(xié)議標(biāo)識符是ws(如果加密最铁,則為wss),服務(wù)器網(wǎng)址就是 URL垮兑。
二:定時器setInterval+cookie
在頁面A設(shè)置一個使用?setInterval?定時器不斷刷新冷尉,檢查?Cookies?的值是否發(fā)生變化,如果變化就進(jìn)行刷新的操作系枪。
由于?Cookies?是在同域可讀的雀哨,所以在頁面 B 審核的時候改變?Cookies?的值,頁面 A 自然是可以拿到的。
這樣做確實(shí)可以實(shí)現(xiàn)我想要的功能雾棺,但是這樣的方法相當(dāng)浪費(fèi)資源膊夹。雖然在這個性能過盛的時代,浪費(fèi)不浪費(fèi)也感覺不出來捌浩,但是這種實(shí)現(xiàn)方案割疾,確實(shí)不夠優(yōu)雅。
三:使用localstorage
localstorage是瀏覽器多個標(biāo)簽共用的存儲空間嘉栓,所以可以用來實(shí)現(xiàn)多標(biāo)簽之間的通信(ps:session是會話級的存儲空間宏榕,每個標(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)行修改時才會觸發(fā)麻昼,當(dāng)前頁面修改localStorage不會觸發(fā)監(jiān)聽函數(shù)。然后就是在對原有的數(shù)據(jù)的值進(jìn)行修改時才會觸發(fā)馋辈,比如原本已經(jīng)有一個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)答毫,這種是可以多個標(biāo)簽頁褥民、iframe共同使用的。
SharedWorker可以被多個window共同使用洗搂,但必須保證這些標(biāo)簽頁都是同源的(相同的協(xié)議消返,主機(jī)和端口號)
首先新建一個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端(暫且這樣稱呼)的代碼就如上,只需注冊一個onmessage監(jiān)聽信息的事件惫叛,客戶端(即使用sharedWorker的標(biāo)簽頁)發(fā)送message時就會觸發(fā)倡勇。
注意webworker無法在本地使用,出于瀏覽器本身的安全機(jī)制嘉涌,所以我這次的示例也是放在服務(wù)器上的妻熊,worker.js和index.html在同一目錄。
因?yàn)榭蛻舳撕蛍ebworker端的通信不像websocket那樣是全雙工的洛心,所以客戶端發(fā)送數(shù)據(jù)和接收數(shù)據(jù)要分成兩步來處理固耘。示例中會有兩個按鈕,分別對應(yīng)的向sharedWorker發(fā)送數(shù)據(jù)的請求以及獲取數(shù)據(jù)的請求词身,但他們本質(zhì)上都是相同的事件--發(fā)送消息厅目。
webworker端會進(jìn)行判斷,傳遞的數(shù)據(jù)為'get'時,就把變量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ù)友绝。