瀏覽器多標籤頁通訊

瀏覽器多標籤頁通訊有助於下降服務器負載,提升運營人員的工做效率,提升用戶體驗。是前端開發優化的一個重要環節。

需求來源

在多數CMS(內容管理系統)後臺上,常見的是一個文章列表頁面,點擊列表項會打開一個新的文章詳情頁面。編輯人員常常在這個詳情頁面上對文章操做,好比修改標題、配圖、摘要等內容。操做完畢以後,因爲文章頁和列表頁是兩個頁面,文章內容數據不能及時同步到列表,這樣就照成運營人員屢次誤操做,這大大下降了運營人員的工做效率。javascript

對於前端工程師來說,實現瀏覽器多個頁卡之間的通訊,及時更新相關數據更改,是一件重要的事情。
圖片描述html

例若有一個需求:當文章詳情頁面更新的時候,會同步到文章列表頁。前端

實現方式

方式一:cookie+setInterval

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+setInterval的不足

一、cookie空間有限,瀏覽器在每個域名下最多能設置30-50個cookie,容量最多4K左右。
二、每次HTTP請求會把當前域的cookie發送到服務器上,而有些cookie只是瀏覽器才用的到,浪費網絡帶寬。
三、setInterval的頻率設置,過大會影響瀏覽器性能,太小會影響時效性。chrome

cookie+setInterval的優勢:

兼容性好,幾乎全部的瀏覽器都支持。小程序

方式二:localStorage

圖片描述

在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)代碼,一樣是不會觸發監聽函數的。

localStorage的不足

一、瀏覽器的容量大小不統一(比cookie大不少了),而且在高版本的瀏覽器才支持localStorage這個屬性
二、目前全部的瀏覽器中都會把localStorage的值類型限定爲string類型,須要JSON轉換。
三、localStorage本質上是對字符串的讀取,若是存儲內容多的話會消耗內存空間,會致使頁面變卡。
四、localStorage只能監聽非己頁面的數據變化,這一點嚴重影響使用。

localStorage的優勢

一、解決了cookie容量小和時效性不足的問題。

方式三:WebSocket

圖片描述

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。

WebSocket的不足:

一、它須要服務端的支持才能完成任務。若是socket數據量比較大的話,會嚴重消耗服務器的資源。

WebSocket的優勢:

一、使用簡單,功能靈活、強大,若是部署了WebSocket服務器,能夠實現不少實時的功能。

方式四:BroadcastChannel

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);

BroadcastChannel的不足:

一、兼容性極差,只支持最新版的Chrome和Firefox,徹底不支持IE和Safari。

BroadcastChannel的優勢:

一、使用簡單,功能單一,跨頁面通訊的理想選擇。

方式五:SharedWorker

圖片描述

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同時使用,但必須保證這些標籤頁都是同源的(相同的協議,主機和端口號)。

SharedWorker的不足:

一、兼容性較差,IE徹底不支持,chrome和Firefox支持很完善,Safari部分支持,若是你的業務是內部系統,不考慮IE,可使用。
二、API比較簡單,配置繁瑣,使用起來仍是比較麻煩。

SharedWorker的優勢:

一、功能強大,不限於瀏覽器通訊,還有共享數據,方法等功能。因爲是另啓的一個新線程,不影響主線程代碼業務,性能優秀,無需藉助服務器,是一個完美的跨頁面通訊解決方案。

咱們作了什麼?(劃重點)

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 大鵬哥

相關文章
相關標籤/搜索