瀏覽器多標籤頁通訊有助於下降服務器負載,提升運營人員的工做效率,提升用戶體驗。是前端開發優化的一個重要環節。
在多數CMS(內容管理系統)後臺上,常見的是一個文章列表頁面,點擊列表項會打開一個新的文章詳情頁面。編輯人員常常在這個詳情頁面上對文章操做,好比修改標題、配圖、摘要等內容。操做完畢以後,因爲文章頁和列表頁是兩個頁面,文章內容數據不能及時同步到列表,這樣就照成運營人員屢次誤操做,這大大下降了運營人員的工做效率。javascript
對於前端工程師來說,實現瀏覽器多個頁卡之間的通訊,及時更新相關數據更改,是一件重要的事情。html
例若有一個需求:當文章詳情頁面更新的時候,會同步到文章列表頁。前端
cookie最初是在客戶端用於存儲用戶的會話信息的。因爲HTTP是一種無狀態的協議,服務器單從網絡鏈接上沒法知道客戶身份。經過cookie就給客戶端們頒發一個通行證,每人一個,不管誰訪問都必須攜帶本身通行證。這樣服務器就能從通行證上確認客戶身份了。cookie其實是一小段的文本信息。客戶端請求服務器,若是服務器須要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個cookie。客戶端瀏覽器會把cookie保存起來。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該cookie一同提交給服務器。服務器檢查該cookie,以此來辨認用戶狀態。服務器還能夠根據須要修改cookie的內容。html5
在JavaScript中,cookie的操做接口即document.cookie,經過這個接口能夠讀取、寫入、刪除cookie。這個操做其實不太友好,因此不少工具庫提供了cookie的操做方法。我這裏提供一個簡單的封裝方法。java
var QQ = {}; QQ.Cookie={ set:function(name,value,expires,path,domain){ if(typeof expires=="undefined"){ expires=new Date(new Date().getTime()+3600*1000); } document.cookie=name+"="+escape(value)+((expires)?"; expires="+expires.toGMTString():"")+((path)?"; path="+path:"; path=/")+((domain)?";domain="+domain:""); }, get:function(name){ var arr=document.cookie.match(new RegExp("(^| )"+name+"=([^;]*)(;|$)")); if(arr!=null){ return unescape(arr[2]); } return null; }, clear:function(name,path,domain){ if(this.get(name)){ document.cookie=name+"="+((path)?"; path="+path:"; path=/")+((domain)?"; domain="+domain:"")+";expires=Fri, 02-Jan-1970 00:00:00 GMT"; } } };
cookie有個特性,一個頁面產生的cookie能被與這個頁面的同一目錄或者其餘子目錄下的頁面訪問,這樣頁面之間就產生了一個共享的存儲空間。一般把cookie的path設置爲一個更高級別的目錄,好比默認「/」,從而使更多的頁面共享cookie,實現多頁面之間相互通訊。 cookie所在的域,默認爲請求的地址,也能夠經過設置document.domain爲父域等方式擴大cookie可被訪問的域。git
列表頁經過setInterval定時器循環監聽cookie的數據變更
列表頁代碼:github
window.onload=function(){ var tid = ''; setInterval(function(){ if(tid != QQ.Cookie.get("tid")){ alert('數據更新!'); tid = QQ.Cookie.get("tid") } }, 1000); }
當詳情頁有數據修改時後,寫入cookie
詳情頁代碼:web
<input id="content" type="text"> <button id="btn">Click</button> <script> window.onload=function(){ var oBtn=document.getElementById("btn"); var oInput=document.getElementById("content"); oBtn.onclick=function(){ var val=oInput.value; QQ.Cookie.set("tid",val); } } </script>
一、cookie空間有限,瀏覽器在每個域名下最多能設置30-50個cookie,容量最多4K左右。
二、每次HTTP請求會把當前域的cookie發送到服務器上,而有些cookie只是瀏覽器才用的到,浪費網絡帶寬。
三、setInterval的頻率設置,過大會影響瀏覽器性能,太小會影響時效性。chrome
兼容性好,幾乎全部的瀏覽器都支持。小程序
在HTML5中,新加入了一個localStorage特性,這個特性主要是用來做爲本地存儲來使用的,解決了cookie存儲空間不足的問題,localStorage中通常瀏覽器支持的是5M大小,這個在不一樣的瀏覽器中localStorage會有所不一樣。
localStorage的API也很簡單,提供了JS的讀寫操做。
if(!window.localStorage){ alert("瀏覽器不支持localstorage"); return false; }else{ var storage = window.localStorage; //經過屬性寫入a字段 storage.a = 1; //經過方法寫入b字段 storage.setItem("b",2); storage.getItem("a"); storage.b; storage.clear(); }
它還比cookie多了一個優勢,提供了onstorage以及storage事件,能夠綁定一個回調函數,使用以下:
window.onstorage = function(e){console.log(e)} // 或者 window.addEventListener('storage', function(){ console.log(e)})
localStorage是Storage對象的實例。對Storage對象進行任何修改,都會在觸發storage事件。當經過屬性或者setItem()方法保存數據,或者使用delete操做符或removeItem()刪除數據,或者調用clear()方法時,都會觸發該事件。經過這個事件,咱們能夠實現頁卡之間的變更監聽。
列表頁經過storage監聽localStorage的數據變更
列表頁代碼:
<script> window.addEventListener("storage",function(event){ console.log("newValue is"+localStorage.getItem("tid")); console.log("oldValue is"+event.oldValue); window.alert('數據更新!'); },false); </script>
當詳情頁有數據修改時後,寫入localStorage
詳情頁代碼:
<input id="content" type="text"/> <button id="btn">Click</button> <script> window.onload=function(){ var oBtn=document.getElementById("btn"); var oInput=document.getElementById("content"); oBtn.onclick=function(){ var val=oInput.value; localStorage.setItem("tid",val); } } </script>
不過,onstorage以及storage事件,針對都是非當前頁面對localStorage進行修改時纔會觸發,當前頁面修改localStorage不會觸發監聽函數。還有就是在對原有的數據的值進行修改時纔會觸發,好比本來已經有一個key爲a,值爲1的localStorage,再執行:localStorage.setItem('a', 1)代碼,一樣是不會觸發監聽函數的。
一、瀏覽器的容量大小不統一(比cookie大不少了),而且在高版本的瀏覽器才支持localStorage這個屬性
二、目前全部的瀏覽器中都會把localStorage的值類型限定爲string類型,須要JSON轉換。
三、localStorage本質上是對字符串的讀取,若是存儲內容多的話會消耗內存空間,會致使頁面變卡。
四、localStorage只能監聽非己頁面的數據變化,這一點嚴重影響使用。
一、解決了cookie容量小和時效性不足的問題。
WebSocket API是下一代客戶端--服務器的異步通訊方法,已被W3C進行了標準化。WebSocket API最偉大之處在於服務器和客戶端能夠雙向實時通訊。WebSocket並不限於以Ajax(或XHR)方式通訊,由於Ajax技術須要客戶端發起請求,而WebSocket服務器和客戶端能夠彼此相互推送信息;XHR受到域的限制,而WebSocket容許跨域通訊。
它的使用很簡單,以下:
// 建立一個Socket實例 var socket = new WebSocket('ws://localhost:8080'); // 打開Socket socket.onopen = function(event) { // 發送一個初始化消息 socket.send('I am the client and I\'m listening!'); // 監聽消息 socket.onmessage = function(event) { console.log('Client received a message',event); }; // 監聽Socket的關閉 socket.onclose = function(event) { console.log('Client notified socket has closed',event); }; // 關閉Socket.... socket.close() };
WebSocket提供了send方法和onmessage事件,用來發送和接收數據。onmessage事件提供了一個data屬性,它能夠包含消息的Body部分。消息的Body部分必須是一個字符串,能夠進行序列化/反序列化操做,以便傳遞更多的數據。
列表頁經過onmessage監聽socket服務器發送過來的消息
列表頁代碼:
<script> var socket = new WebSocket('ws://localhost:8080'); socket.onopen = function(event) { socket.onmessage = function(event) { console.log('Client received a message', event); }; }; </script>
當詳情頁有數據修改時後,經過socket鏈接,通知列表頁更新數據。
詳情頁代碼:
<input id="content" type="text"/> <button id="btn">Click</button> <script> var socket = new WebSocket('ws://localhost:8080'); window.onload=function(){ var oBtn=document.getElementById("btn"); var oInput=document.getElementById("content"); oBtn.onclick=function(){ var val=oInput.value; socket.onopen = function(event) { // 發送數據類型必須是string、ArrayBuffer、Blob之一 socket.send('數據更新!'); } } </script>
WebSocket的語法很是簡單,不過須要IE10+瀏覽器才支持WebSocket通訊。若是你的業務須要兼容IE8,9。業界一般使用第三方庫來解決這個問題,好比Socket.IO,它使用檢測功能來判斷是否創建WebSocket鏈接,或者是AJAX long-polling鏈接,或Flash等,可快速建立實時的應用程序。Socket.IO還提供了一個NodeJS API,它看起來很是像瀏覽器的API。
一、它須要服務端的支持才能完成任務。若是socket數據量比較大的話,會嚴重消耗服務器的資源。
一、使用簡單,功能靈活、強大,若是部署了WebSocket服務器,能夠實現不少實時的功能。
BroadcastChannel即廣播頻道,是window下面的一個API,該API是用於同源不一樣頁面之間完成通訊的功能。咱們能夠理解它是一個廣播臺,全部的廣播實例,都會接入這個廣播臺(中介者模式中的控制中心),因此,只要在初始化實例時,傳入相同的頻道值,就會被接入到一個相同的廣播頻道中。它的實現最簡單,不少第三方JS庫都實現了一套本身的BroadcastChannel。
列表頁經過onmessage監聽其餘頁面發送過來的消息
列表頁代碼:
// 接收廣播 let articleCast = new BroadcastChannel('mychannel'); articleCast.onmessage = function (e) { console.log(e.data); }
當詳情頁有數據修改時後,經過postMessage,傳遞數據。
詳情頁代碼:
// 建立廣播併發送 let listCast = new BroadcastChannel('mychannel'); myObj = { tid: "123", title: "更改後的標題" }; listCast.postMessage(myObj);
一、兼容性極差,只支持最新版的Chrome和Firefox,徹底不支持IE和Safari。
一、使用簡單,功能單一,跨頁面通訊的理想選擇。
SharedWorker也是HTML5提供的新的瀏覽器API,叫共享工做線程。它容許多個頁面共享使用線程,每一個頁面都連接到該共享工做線程的某個端口號上。頁面經過該端口與共享工做線程進行通訊。目前的Web全部程序的操做都基於頁面的,而SharedWorker的引入開闢了一個「Web程序」在後臺線程的概念。並且它還能夠和頁面交互,至關於把全部頁面都聚攏起來了。上例講爲每一個頁面都維護一份WebSocket代碼不只耗費大量的鏈接數,並且還拖慢性能。這些通用的鏈接最好固然作成可跨域頁面共用的,在SharedWorker引入以前並無一個完美的跨頁面通訊解決方案。
列表頁經過onmessage監聽SharedWoker發送過來的消息
列表頁代碼:
<script> var s = new SharedWorker('x.js'); s.port.onmessage = function(e){ console.log(e.data); window.alert("數據變化!") }; s.port.start(); </script>
當詳情頁有數據修改時後,經過SharedWorker,通知列表頁更新數據。
<input id="content" /><input type="button" id="btn" value="發送" /> <script> var s = new SharedWorker('x.js'); btn.onclick=function(){ s.port.postMessage(document.getElementById('content').value); }; s.port.start(); </script>
其中共享線程x.js的代碼也很簡單,它的工做是雙向的,每個頁面均可以用來接收和發送數據。
//x.js var pool = []; onconnect = function(e) { pool.push(e.ports[0]); e.ports[0].onmessage = function(e){ for(var i=0; i<pool.length; i++) pool[i].postMessage(e.data); }; };
SharedWorker就像運行在瀏覽器後端的守衛者,能夠被多個window同時使用,但必須保證這些標籤頁都是同源的(相同的協議,主機和端口號)。
一、兼容性較差,IE徹底不支持,chrome和Firefox支持很完善,Safari部分支持,若是你的業務是內部系統,不考慮IE,可使用。
二、API比較簡單,配置繁瑣,使用起來仍是比較麻煩。
一、功能強大,不限於瀏覽器通訊,還有共享數據,方法等功能。因爲是另啓的一個新線程,不影響主線程代碼業務,性能優秀,無需藉助服務器,是一個完美的跨頁面通訊解決方案。
SharedWorker提供的API不多,使用比較簡單,若是須要完成複雜的頁面通訊,仍是有必定難度。基於此,我實現了一款基於SharedWorker的封裝庫,叫做superSharedWorker
它是一款頁面之間通訊的JavaScript框架,它經過shared worker 實現純瀏覽器頁卡之間的通訊。你無需瞭解shared worker,能夠快速使用頁面之間的數據傳遞,快捷,強大。它的優勢就是經過原生JS實現,無需依賴任何JS庫實現了對sharedWorker的封裝。開箱即用,配置簡單。
兩種使用方式:
一、ES6 import的方式
import superSharedWorker from './src/index.js'; let superSharedWorker = new superSharedWorker('page1', callback); //註冊 superSharedWorker.send('hello world!'); //發送消息
二、script標籤外鏈的形式
<script src="./build/super-sharedworker.js"></script> <script type="text/javascript"> //<!-- var superSharedWorker = new SuperShared('page1', onRecvMsg); function onRecvMsg(message) { console.log(message) } superSharedWorker.send('hello, world'); //--> </script>
更多用法舉例:
let superSharedWorker = new superSharedWorker('page1', callback); //註冊 superSharedWorker.add({name:'chunpengliu', sex:1}); superSharedWorker.del('sex');// 刪除緩衝區數據 superSharedWorker.send({'time':2019}, 'page2'); //一次性發送緩衝區數據,只發送給name="page2"的頁面 superSharedWorker.close(); //關閉線程,節省資源
它提供了不少強大的功能,可一對一,一對多發送消息。像使用git同樣傳遞數據。
經過討論,實現了四種實現瀏覽器標籤頁之間的通訊,分別是使用cookie、使用websocket協議、經過localstorage、以及使用html5瀏覽器的新特性SharedWorker,每種方法各有利弊。若是不考慮兼容舊的瀏覽器,superSharedWorker 或許是最好的解決方案,優化使用效率,提高用戶體驗,趕快使用瀏覽器多標籤頁通訊功能吧!
最後,TNFE團隊爲前端開發人員整理出了小程序以及web前端技術領域的最新優質內容,每週更新✨,歡迎star,github地址:https://github.com/Tnfe/TNFE-Weekly
做者:TNFE 大鵬哥