實現多個標籤頁之間通訊的幾種方法

效果圖.gif
示例地址

prologue

  • 以前在網上看到一個面試題:如何實現瀏覽器中多個標籤頁之間的通訊。我目前想到的方法有三種:使用websocket協議、經過localstorage、以及使用html5瀏覽器的新特性SharedWorker。
  • websocket這裏先不介紹了,全雙工(full-duplex)通訊天然能夠實現多個標籤頁之間的通訊,相信網上經過websocket實現聊天室的教程也很多,我本身也用socket.io寫了一個在線聊天室
  • 接下來會介紹另外兩個方法:監聽localstorage和使用SharedWorker

localstorage

  • localstorage是瀏覽器多個標籤共用的存儲空間,因此能夠用來實現多標籤之間的通訊(ps:session是會話級的存儲空間,每一個標籤頁都是單獨的)。
  • 直接在window對象上添加監聽便可:
window.onstorage = (e) => {console.log(e)}
// 或者這樣
window.addEventListener('storage', (e) => console.log(e))
複製代碼
  • onstorage以及storage事件,針對都是非當前頁面對localStorage進行修改時纔會觸發,當前頁面修改localStorage不會觸發監聽函數。而後就是在對原有的數據的值進行修改時纔會觸發,好比本來已經有一個key會a值爲b的localStorage,你再執行:localStorage.setItem('a', 'b')代碼,一樣是不會觸發監聽函數的。

webworker

  • 咱們都知道JavaScript是單線程的,可是瀏覽器是擁有過個線程的好比:gui渲染線程、JS引擎線程、事件觸發線程、異步http請求線程。
  • webworker做爲瀏覽器的一個新特性,能夠提供一個額外的線程來執行一些js代碼,而且不會影響到瀏覽器用戶界面。
  • 應用場景:好比頁面中包含耗時較大的算法代碼時,就會阻塞線程影響瀏覽器渲染等等。這時候就可把耗時代碼,放到webworker(另外一個線程)中執行。
  • 注意,這種多線程能力不是JavaScript語言原生具備的,而是瀏覽器宿主環境提供的。
  • 普通的webworker直接使用new Worker()便可建立,這種webworker是當前頁面專有的。而後還有種共享worker(SharedWorker),這種是能夠多個標籤頁、iframe共同使用的,接下來介紹如何使用SharedWorker實現標籤頁之間的通訊。

SharedWorker

  • SharedWorker能夠被多個window共同使用,但必須保證這些標籤頁都是同源的(相同的協議,主機和端口號)
  • 首先新建一個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監聽信息的事件,客戶端(即便用sharedWorker的標籤頁)發送message時就會觸發。html

  • 注意webworker沒法在本地使用,出於瀏覽器自己的安全機制,因此我此次的示例也是放在服務器上的,worker.jsindex.html在同一目錄。 html5

    image

  • 由於客戶端和webworker端的通訊不像websocket那樣是全雙工的,因此客戶端發送數據和接收數據要分紅兩步來處理。示例中會有兩個按鈕,分別對應的向sharedWorker發送數據的請求以及獲取數據的請求,但他們本質上都是相同的事件--發送消息。web

  • webworker端會進行判斷,傳遞的數據爲'get'時,就把變量data的值回傳給客戶端,其餘狀況,則把客戶端傳遞過來的數據存儲到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的數據:', e.data)
      }, false)
      worker.port.start()
      window.worker = worker
    }
// 獲取和發送消息都是調用postMessage方法,我這裏約定的是傳遞'get'表示獲取數據。
window.worker.port.postMessage('get')
window.worker.port.postMessage('發送信息給worker')
複製代碼
  • 頁面A發送數據給worker,而後打開頁面B,調用window.worker.port.postMessage('get'),便可收到頁面A發送給worker的數據。
  • 參考:developer.mozilla.org/zh-CN/docs/…
相關文章
相關標籤/搜索