效果圖.gif
prologue
- 之前在網(wǎng)上看到一個面試題:如何實現(xiàn)瀏覽器中多個標簽頁之間的通信。我目前想到的方法有三種:使用websocket協(xié)議刃跛、通過localstorage膊夹、以及使用html5瀏覽器的新特性SharedWorker怔匣。
- websocket這里先不介紹了,全雙工(full-duplex)通信自然可以實現(xiàn)多個標簽頁之間的通信,相信網(wǎng)上通過websocket實現(xiàn)聊天室的教程也不少苔严,我自己也用socket.io寫了一個在線聊天室.
- 接下來會介紹另外兩個方法:監(jiān)聽localstorage和使用SharedWorker
localstorage
- localstorage是瀏覽器多個標簽共用的存儲空間,所以可以用來實現(xiàn)多標簽之間的通信(ps:session是會話級的存儲空間孤澎,每個標簽頁都是單獨的)届氢。
- 直接在window對象上添加監(jiān)聽即可:
window.onstorage = (e) => {console.log(e)}
// 或者這樣
window.addEventListener('storage', (e) => console.log(e))
- onstorage以及storage事件,針對都是非當前頁面對localStorage進行修改時才會觸發(fā)覆旭,當前頁面修改localStorage不會觸發(fā)監(jiān)聽函數(shù)退子。然后就是在對原有的數(shù)據(jù)的值進行修改時才會觸發(fā)岖妄,比如原本已經(jīng)有一個key會a值為b的localStorage,你再執(zhí)行:
localStorage.setItem('a', 'b')
代碼寂祥,同樣是不會觸發(fā)監(jiān)聽函數(shù)的荐虐。
webworker
- 我們都知道JavaScript是單線程的,但是瀏覽器是擁有過個線程的比如:gui渲染線程丸凭、JS引擎線程福扬、事件觸發(fā)線程、異步http請求線程惜犀。
- webworker作為瀏覽器的一個新特性忧换,可以提供一個額外的線程來執(zhí)行一些js代碼,并且不會影響到瀏覽器用戶界面向拆。
- 應用場景:比如頁面中包含耗時較大的算法代碼時亚茬,就會阻塞線程影響瀏覽器渲染等等。這時候就可把耗時代碼浓恳,放到webworker(另一個線程)中執(zhí)行刹缝。
- 注意,這種多線程能力不是JavaScript語言原生具有的颈将,而是瀏覽器宿主環(huán)境提供的梢夯。
- 普通的webworker直接使用
new Worker()
即可創(chuàng)建,這種webworker是當前頁面專有的晴圾。然后還有種共享worker(SharedWorker)颂砸,這種是可以多個標簽頁、iframe共同使用的死姚,接下來介紹如何使用SharedWorker實現(xiàn)標簽頁之間的通信人乓。
SharedWorker
- SharedWorker可以被多個window共同使用,但必須保證這些標簽頁都是同源的(相同的協(xié)議都毒,主機和端口號)
- 首先新建一個js文件
worker.js
色罚,具體代碼如下:
// sharedWorker所要用到的js文件,不必打包到項目中账劲,直接放到服務器即可
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的標簽頁)發(fā)送message時就會觸發(fā)瀑焦。
-
注意webworker無法在本地使用腌且,出于瀏覽器本身的安全機制,所以我這次的示例也是放在服務器上的榛瓮,
worker.js
和index.html
在同一目錄铺董。
image 因為客戶端和webworker端的通信不像websocket那樣是全雙工的,所以客戶端發(fā)送數(shù)據(jù)和接收數(shù)據(jù)要分成兩步來處理榆芦。示例中會有兩個按鈕柄粹,分別對應的向sharedWorker發(fā)送數(shù)據(jù)的請求以及獲取數(shù)據(jù)的請求喘鸟,但他們本質(zhì)上都是相同的事件--發(fā)送消息。
webworker端會進行判斷驻右,傳遞的數(shù)據(jù)為'get'時什黑,就把變量data的值回傳給客戶端,其他情況堪夭,則把客戶端傳遞過來的數(shù)據(jù)存儲到data變量中愕把。下面是客戶端的代碼:
// 這段代碼是必須的,打開頁面后注冊SharedWorker森爽,顯示指定worker.port.start()方法建立與worker間的連接
if (typeof Worker === "undefined") {
alert('當前瀏覽器不支持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ù)付呕。 - 參考:https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers