自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

六種瀏覽器跨窗口通信方案 - 從實(shí)際案例出發(fā)

系統(tǒng) 瀏覽器
各種方式都可以實(shí)現(xiàn)通知列表頁(yè)去做刷新動(dòng)作,不過更推薦使用 window.opener? 或 BroadcastChannel 來實(shí)現(xiàn),這兩種方式相對(duì)使用簡(jiǎn)單并且很符合這個(gè)業(yè)務(wù)場(chǎng)景。

瀏覽器跨窗口通信,聽上去挺有技術(shù)感的,但實(shí)現(xiàn)起來方案倒是挺多的。本文將從一個(gè)實(shí)際的業(yè)務(wù)開發(fā)場(chǎng)景,帶你了解如何實(shí)現(xiàn)瀏覽器跨窗口通信。

業(yè)務(wù)場(chǎng)景

一個(gè)常規(guī)的業(yè)務(wù)列表頁(yè),頁(yè)面中提供了一個(gè)新增功能,由于新增功能的表單項(xiàng)內(nèi)容比較多,所以交互上使用新開一個(gè)窗口來完成。這時(shí)問題來了,在新增完成后,如何通知列表頁(yè)面刷新列表數(shù)據(jù),以便展示出剛才新增的那一條數(shù)據(jù)。

圖片圖片

各位可以先自己在心中簡(jiǎn)單想想,如果讓你實(shí)現(xiàn)這個(gè)功能,你會(huì)使用什么方案。

解決方案

window.opener

window.opener 代表的是打開當(dāng)前窗口的那個(gè)窗口的引用,例如:在 window A 中打開了 window B,B.opener 返回 A。

有了這個(gè)引用關(guān)系,我們就可以實(shí)現(xiàn)跨窗口通信:

圖片圖片

  1. 列表頁(yè)設(shè)置刷新列表方法 window.refreshList = () => {}
  2. 列表頁(yè)通過 window.open 或者 <a href="newUrl" target="_blank" rel="opener">新增</a> 打開新增功能頁(yè)面。
  3. 用戶完成新增表單填寫并提交,通過調(diào)用 window.opener.refreshList 方法來刷新列表頁(yè)面數(shù)據(jù),并關(guān)閉當(dāng)前頁(yè)。

有人可能注意到了,在 a 標(biāo)簽中我們使用到了 rel="opener" 屬性,為什么要設(shè)置這個(gè)屬性呢?

rel 屬性定義了所鏈接的資源與當(dāng)前文檔的關(guān)系,常見的屬性值有:

  • opener: 打開的新頁(yè)面 window.opener 會(huì)指向前一個(gè)頁(yè)面的 window。
  • noopener: 和 opener 相對(duì)應(yīng), window.opener 為空。
  • noreferrer:打開新頁(yè)面時(shí)請(qǐng)求頭不會(huì)包含 Referer,比如你未設(shè)置 noreferrer 的情況下,從 antd 打開百度,百度頁(yè)面請(qǐng)求頭就有 Referer: https://ant.design/
  • nofollow: 主要用于 SEO,告訴搜索引擎忽略該鏈接。

主要關(guān)注 opener 和 noopener 屬性,a 標(biāo)簽?zāi)J(rèn)情況下 rel=noopener,這代表打開的新增頁(yè)面的 window.opener 對(duì)象會(huì)為空,所以需要設(shè)置 rel=opener。

那么又有一個(gè)問題,為什么 a 標(biāo)簽?zāi)J(rèn)是 rel=noopener, 因?yàn)?nbsp;opener 存在安全漏洞,比如你以 opener 的方式打開了一個(gè)未知的新頁(yè)面,這個(gè)新頁(yè)面可以通過 window.opener.location.href = 'fake.com' 重定向你的頁(yè)面。

而 window.open 默認(rèn)情況下 rel=opener,故打開的新頁(yè)面可以拿到 window.opener 對(duì)象,不過要是打開第三方未知網(wǎng)站,建議設(shè)置為 noopener, 比如 window.open('https://baidu.com', 'title', 'noopener,noreferrer')。

BroadcastChannel

BroadcastChannel API 顧名思義,為“廣播頻道”,適用于在同一域名下的多個(gè)窗口、標(biāo)簽頁(yè)或 iframe 之間進(jìn)行實(shí)時(shí)消息廣播。它的使用也非常簡(jiǎn)單,我們也看下如何通過它來實(shí)現(xiàn)上面的業(yè)務(wù)場(chǎng)景。

列表頁(yè)創(chuàng)建一個(gè) BroadcastChannel 實(shí)例來監(jiān)聽消息:

// 創(chuàng)建 BroadcastChannel 實(shí)例
const channel = new BroadcastChannel('myChannel');

// 監(jiān)聽廣播通道的消息
channel.addEventListener('message', (event) => {
  console.log('接收:', event.data); // { action: 'refresh' }
})

新增功能頁(yè)面同樣創(chuàng)建一個(gè) BroadcastChannel 實(shí)例,頻道名稱需要和列表頁(yè)一致:

// 創(chuàng)建 BroadcastChannel 實(shí)例
const channel = new BroadcastChannel('myChannel');
// 向廣播通道發(fā)送消息
channel.postMessage({ action: 'refresh' });
// 關(guān)閉頻道
channel.close()

可以看到 BroadcastChannel 的使用很簡(jiǎn)單,雙方創(chuàng)建同名頻道的 BroadcastChannel 實(shí)例,然后一方監(jiān)聽 message 事件,一方使用 postMessage 廣播內(nèi)容。

postMessage

對(duì)于 postMessage,我們最常用的方式應(yīng)該就是當(dāng)前頁(yè)面和 iframe 的跨域消息通信了,其實(shí)它也能完成跨窗口通信,核心就是能拿到新窗口的 window 對(duì)象,這個(gè)在 window.opener 方案中我們就知道通過設(shè)置 rel="opener" 就可以辦到。

列表頁(yè)打開新窗口,并監(jiān)聽 message 事件:

<a href="./add.html" target="_blank" rel="opener">新增</a>
<script>
  // 不同與 BroadcastChannel,這邊是監(jiān)聽 window 上的 message 事件
  window.addEventListener("message", receiveMessage);
  function receiveMessage(event) {
    console.log('接收:', event.data); // { action: 'refresh' }
  }
</script>

新增功能頁(yè)面使用 window.opener.postMessage 發(fā)送消息:

window.opener?.postMessage({ action: 'refresh' }, '*');

至此我們已經(jīng)完成了上面的業(yè)務(wù)需求,postMessage 的優(yōu)勢(shì)在于可以跨域。

MessageChannel

MessageChannel API 允許我們創(chuàng)建一個(gè)新的消息通道,并通過它的兩個(gè) MessagePort 實(shí)例屬性發(fā)送數(shù)據(jù),同時(shí)它也可以跨域通信。

不同于 BroadcastChannel 的廣播,MessageChannel 只提供雙向通信通道,不過它既可以像 postMessage 一樣用于 iframe 通信,也可以用于 Web Worker 之間進(jìn)行通信。

圖片圖片

要用 MessageChannel 實(shí)現(xiàn)跨窗口通信,方式有點(diǎn)類似 postMessage, 打開新頁(yè)面時(shí)需要設(shè)置 rel="opener"。

列表頁(yè)初始化 MessageChannel 實(shí)例,并在 port1 上監(jiān)聽 message 事件:

// 創(chuàng)建 MessageChannel 實(shí)例
window.messageChannel = new MessageChannel();
const port1 = window.messageChannel.port1;
// port1 監(jiān)聽 message 事件
port1.onmessage = function(event) {
  console.log('接收:', event.data); // { action: 'refresh' }
};

新增功能頁(yè)面使用 window.opener.messageChannel 拿到列表的 MessageChannel 實(shí)例,并使用 port2 的 postMessage 方法往 port1 通道上發(fā)送消息:

// 獲取 MessageChannel 實(shí)例
const messageChannel = window.opener.messageChannel;
const port2 = messageChannel.port2;
// 往 port1 發(fā)送消息
port2.postMessage({ action: 'refresh' });

需要注意的是 MessagePort 對(duì)象如果使用 addEventListener 監(jiān)聽 message 事件,就需要調(diào)用下 port.start() 方法,使用 onmessage 則可以不需要。

storage 事件

當(dāng) localStorage 或 sessionStorage 被修改時(shí),將觸發(fā) storage 事件,利用這個(gè)機(jī)制,我們也可以完成跨窗口通信。同時(shí)因?yàn)橛玫氖?nbsp;localStorage 或 sessionStorage 方式,所以頁(yè)面必須是同一域名下。

值得注意的是,sessionStorage 并不能滿足上面的業(yè)務(wù)需求,sessionStorage 要想觸發(fā) storage 事件,必須在同一窗口,也就是一般只在當(dāng)前頁(yè)和其加載的同域名 iframe 下使用。還有一點(diǎn)就是當(dāng)前頁(yè)的 setItem 不會(huì)觸發(fā)當(dāng)前頁(yè)的 storage 事件,只會(huì)觸發(fā)其他窗口的。

列表頁(yè)監(jiān)聽 storage 事件,判斷是否是對(duì)應(yīng) key 值發(fā)生變化:

window.addEventListener("storage", () => {
  console.log('發(fā)生變化的值:', event.key); 
  if (event.key === 'refresh') {
     // 刷新列表
  }
});

新增功能頁(yè)面使用 localStorage 的 setItem 來觸發(fā)列表的 storage 事件:

window.localStorage.setItem('fresh', Date.now())

SharedWorker

SharedWorker 是 Web Workers API 的一種擴(kuò)展,它允許在多個(gè)瀏覽器上下文中(例如多個(gè)頁(yè)面或多個(gè) iframe )共享一個(gè) Worker。ShareWorker 遵守同源策略,也就是必須在同一域名下使用 SharedWorker。

先寫個(gè) worker.js 腳本:

const ports = [];

onconnect = function (e) {
  const port = e.ports[0];
  ports.push(port);
  port.onmessage = function (e) {
    console.log("worker接收到的消息:", e.data);
    ports.forEach((p) => {
      p.postMessage(e.data);
    });
  };
};

列表頁(yè)創(chuàng)建 ShareWorker 實(shí)例,然后監(jiān)聽 message 事件:

const sharedWorker = new SharedWorker('./worker.js', 'test');
const port = sharedWorker.port;
port.onmessage = function (event) {
  console.log('接收:', event.data); // { action: 'refresh' }
};

新增功能頁(yè)面使用 postMessage 發(fā)送消息給 worker,worker 在發(fā)送給各個(gè)主線程:

const sharedWorker = new SharedWorker('./worker.js', 'test');
const port = sharedWorker.port;
port.postMessage({ action: 'refresh' });

這樣我們就完成了上述的業(yè)務(wù)需求。

總結(jié)

上述的各種方式都可以實(shí)現(xiàn)通知列表頁(yè)去做刷新動(dòng)作,不過更推薦使用 window.opener 或 BroadcastChannel 來實(shí)現(xiàn),這兩種方式相對(duì)使用簡(jiǎn)單并且很符合這個(gè)業(yè)務(wù)場(chǎng)景。

對(duì)于其他需要跨窗口通信的場(chǎng)景,可以根據(jù)各個(gè) API 的能力特點(diǎn)來選擇使用哪個(gè)。

責(zé)任編輯:武曉燕 來源: 栗子前端
相關(guān)推薦

2018-04-11 10:51:25

多線程進(jìn)程主線程

2019-01-17 10:58:52

JS異步編程前端

2022-04-07 18:49:56

項(xiàng)目場(chǎng)景數(shù)據(jù)庫(kù)

2012-01-04 16:14:17

2022-05-24 10:43:02

延時(shí)消息分布式MQ

2009-04-17 22:25:16

多核四核CPU

2019-12-03 12:16:36

物聯(lián)網(wǎng)ZigBee藍(lán)牙低功耗

2019-05-15 08:00:00

vue組件間通信前端

2025-03-17 08:07:11

2017-06-26 10:35:58

前端JavaScript繼承方式

2009-03-12 10:11:00

綜合布線規(guī)劃網(wǎng)絡(luò)布局

2013-12-06 14:57:24

瀏覽器

2010-03-15 17:12:52

Python字典

2019-10-12 01:10:09

物聯(lián)網(wǎng)無線技術(shù)IOT

2010-05-31 10:11:02

2010-09-15 09:12:03

JavaScript瀏覽器兼容

2020-10-14 09:00:00

SAST漏洞攻擊

2009-02-11 09:46:00

ASON網(wǎng)絡(luò)演進(jìn)

2024-11-20 15:24:49

2023-11-28 15:32:30

負(fù)載均衡算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)