JS多頁面間通訊

需求:項目中做了一個音樂播放器,後面Boss說用戶同時打開了多個頁面播放音樂,這個時候每個頁面都在播放音樂,能不能只讓當前用戶操作的頁面播放音樂。

接到這個需求的時候,先去某雲音樂看了一下,用的應該是WebSocket。給領導反饋一下後,領導說後端騰不出資源配合。那隻能先排除這種方案了。

當時的思路是這樣的:

思維過程

具體是:給每一個打開的要播放的網頁鏈接加一個唯一標識參數(uuid),點擊播放的時候,獲取該參數:uuid,並設置該uuid的值,作爲localStorage一個屬性的keyvalue‘play’,並且把該uuid的值,存入一個uuidArray數組裏。那麼當打開第二個頁面並且播放的時候,重複上述動作,但是在localStorage裏將該頁面的狀態設爲play,其他頁面設爲pause,每個頁面監聽storage事件,如果本頁面的狀態值爲play,則可以播放,否則,停止播放。當關閉頁面的時候,清除該localStorage裏存儲的值,並且從uuidArray數組裏刪除該項。

說的挺囉嗦,簡言之,就是監聽localStoragestorage事件,和頁面的關閉事件,去做出不同的反應。

下面是代碼,模擬該情況:

一個主頁,表示頁面會從這裏跳往A、B、C三個頁面;

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>link-index</title>

    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>

<body>
    <button class="a">JUMP TO PAGE A</button>
    <button class="b">JUMP TO PAGE B</button>
    <button class="c">JUMP TO PAGE C</button>

    <script>
       
        $('.a').on('click', function () {
            window.open('link-a.html?uuid=' + uuid(16))
        })

        $('.b').on('click', function () {
            window.open('link-b.html?uuid=' + uuid(16))
        })

        $('.c').on('click', function () {
            window.open('link-c.html?uuid=' + uuid(16))
        })

        
        function uuid(len, radix) {
            var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
            var uuid = [],
                i;
            radix = radix || chars.length;

            if (len) {
                // Compact form
                for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
            } else {
                // rfc4122, version 4 form
                var r;

                // rfc4122 requires these characters
                uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
                uuid[14] = '4';

                // Fill in random data.  At i==19 set the high bits of clock sequence as
                // per rfc4122, sec. 4.1.5
                for (i = 0; i < 36; i++) {
                    if (!uuid[i]) {
                        r = 0 | Math.random() * 16;
                        uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                    }
                }
            }

            return uuid.join('');
        }

    </script>
</body>

</html>

其中主頁的文件名爲:link-index.html

其他三個頁面的文件名爲:link-a.html, link-b.html, link-c.html,內容爲:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <!-- title名稱可以相應的更換 -->
    <title>link-a</title> 

    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>

<body>
    <div>
        <button class="play">PLAY</button>
    </div>
    <script>
        var key = getQueryVariable('uuid')
        
        //設置uuids,以及key
        function getUuids() {
            localStorage.setItem(key, 'play')
            var val = localStorage.getItem(key)
            if (!localStorage.getItem('uuids')) { //如果不存在uuids,則構建數組,將該key push進去
                var uuidArray = []
                uuidArray.push(key)
                localStorage.setItem('uuids', JSON.stringify(uuidArray))
            } else {
             //如果存在uuids數組,並且該key值不在數組內,則添加進uuids數組裏,並重新設置localStorage裏uuids的值
                var uuidArray = localStorage.getItem('uuids');
                var arr = JSON.parse(uuidArray);
                !(arr.indexOf(key) > -1) && arr.push(key)
                localStorage.setItem('uuids', JSON.stringify(arr))
                console.log(localStorage.getItem('uuids'), 'uuids..');
            }
        }

    	//獲取url鏈接上對應參數的值
        function getQueryVariable(variable) {
            var query = window.location.search.substring(1);
            var vars = query.split("&");
            for (var i = 0; i < vars.length; i++) {
                var pair = vars[i].split("=");
                if (pair[0] == variable) {
                    return pair[1];
                }
            }
            return '';
        }
        
        //設置各個頁面的播放狀態
        function setStatus() {
            let arr = JSON.parse(localStorage.getItem('uuids')), len = arr.length;
            for(let i = 0; i < len; i ++) {
                (arr[i] == key)? localStorage.setItem(arr[i], 'play') : localStorage.setItem(arr[i], 'pause')
            }
        }
		
		//檢測到本地存儲發生變化,並且當前頁面狀態爲pause的時候,停止播放音樂
		//此時player爲播放器對象,調用其pause()方法,可停止音樂播放
        window.addEventListener('storage', function (e) {
            let val = localStorage.getItem(getQueryVariable('uuid'))
          (val == 'pause') && player.pause()
        })

        
        //點擊事件發生,播放音樂的時候
        $('.play').on('click', function(){
        	getUuids();
          	setStatus();
        })
		
		//當關閉頁面的時候,從本地存儲裏移除對應項
        window.onbeforeunload = function (event) {
            var event = event || window.event;
            var newArr = JSON.parse(uuidArray)
            var newLocalstorageArray = newArr.filter(function(item, index){
                return item != key
            })
            localStorage.setItem('uuids', JSON.stringify(newLocalstorageArray))
            localStorage.removeItem(key)
        }

    </script>
</body>

</html>

注意點:上面項目,需要在服務器環境下啓動頁面,並且保證他們是同源的。


本文只提供處理問題的一種思路:具體知識點細節可以看下面瞭解。

什麼是同源策略?同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。如果兩個頁面的協議,端口(如果有指定)和主機都相同,表示他們是同源的。

同源的問題,可以看這裏:https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy

關於onstorage事件:該事件不在導致數據變化的當前頁面觸發(如果瀏覽器同時打開一個域名下面的多個頁面,當其中的一個頁面改變 sessionStorage 或 localStorage 的數據時,其他所有頁面的 storage 事件會被觸發,而原始頁面並不觸發 storage 事件)

onstorage事件,可以看這裏:https://developer.mozilla.org/zh-CN/docs/Web/API/WindowEventHandlers/onstorage

關於onbeforeunload:提到onbeforeunload,不得不提到onunload,他們的區別,兼容性問題,可點擊下面鏈接查看。

onbeforeunload事件:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/onbeforeunload

onunlaod事件: https://developer.mozilla.org/zh-CN/docs/Web/Events/unload

onbeforeunload在當前主流瀏覽器兼容良好,safari也支持。


想在localStorage裏存儲數組,怎麼辦?上面代碼已經給出了。


至於其他方案,正在研究中,會找機會寫出。

該方案存在的缺陷:

  1. 比如用戶直接複製link-a.html的鏈接而打開新頁面播放音樂,此時uuid是重複的,按現有邏輯,此時兩個頁面會同時播放音樂。
  2. 上面邏輯在同一瀏覽器裏不同標籤頁的情況下,是成立的。但是如果同時打開了兩個瀏覽器,在每個瀏覽器都點播放的話,也會同時在這兩個頁面播放音樂。

還是覺得WebSocket這種方案好一些,但是目前項目中用的還是上述方案。

如果有更好的方案,而不用通過和後臺交互,歡迎提供,再此先謝過!~

發佈了38 篇原創文章 · 獲贊 39 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章