使用通道訊息

注意:此功能在 Web Workers 中可用。

通道訊息 API允許在同一文件附加的兩個獨立指令碼(例如,兩個<iframe>元素、主文件和單個<iframe>,或透過SharedWorker的兩個文件)直接通訊,透過雙向通道(或管道)在每個端點有一個埠之間傳遞訊息。

在本文中,我們將探討這項技術的基礎知識。

用例

通道訊息主要適用於需要將來自其他網站的功能透過 iframe 嵌入到主介面的社交網站,例如遊戲、通訊錄或帶有個性化音樂選擇的音訊播放器。當它們作為獨立單元執行時,一切都還好,但當您想要主網站和<iframe>元素之間,或不同<iframe>元素之間進行互動時,就會出現困難。例如,如果您想從主網站向通訊錄新增聯絡人,將遊戲中的高分新增到主個人資料,或者從音訊播放器新增新的背景音樂選擇到遊戲中,該怎麼辦?由於 Web 使用的安全模型,使用傳統 Web 技術來實現這些功能並不容易。您需要考慮來源之間是否相互信任,以及訊息如何傳遞。

另一方面,訊息通道可以提供一個安全通道,允許您在不同的瀏覽上下文之間傳遞資料。

注意:有關更多資訊和想法,規格中“Web 上的物件能力模型基礎埠”部分是一篇有用的讀物。

簡單示例

為了幫助您入門,我們在 GitHub 上釋出了一些演示。首先,請檢視我們的通道訊息基本演示(也可以線上執行),它展示了一個頁面和嵌入的<iframe>之間非常簡單的單訊息傳輸。

其次,請檢視我們的多訊息演示(可以線上執行),它展示了一個稍微複雜的設定,可以在主頁面和 IFrame 之間傳送多條訊息。

在本文中,我們將重點介紹後一個示例,該示例看起來像

Demo with "Hello this is my demo" sent as five separate messages. The messages are displayed as a bulleted list.

建立通道

在演示的主頁面中,我們有一個簡單的表單,其中包含一個文字輸入框,用於輸入要傳送到<iframe>的訊息。我們還有一個段落,稍後將用它來顯示將從<iframe>收到的確認訊息。

js
const input = document.getElementById("message-input");
const output = document.getElementById("message-output");
const button = document.querySelector("button");
const iframe = document.querySelector("iframe");

const channel = new MessageChannel();
const port1 = channel.port1;

// Wait for the iframe to load
iframe.addEventListener("load", onLoad);

function onLoad() {
  // Listen for button clicks
  button.addEventListener("click", onClick);

  // Listen for messages on port1
  port1.onmessage = onMessage;

  // Transfer port2 to the iframe
  iframe.contentWindow.postMessage("init", "*", [channel.port2]);
}

// Post a message on port1 when the button is clicked
function onClick(e) {
  e.preventDefault();
  port1.postMessage(input.value);
}

// Handle messages received on port1
function onMessage(e) {
  output.innerHTML = e.data;
  input.value = "";
}

我們首先透過使用MessageChannel()建構函式建立一個新的訊息通道。

當 IFrame 載入完成後,我們為我們的按鈕註冊一個onclick處理程式,併為MessageChannel.port1註冊一個onmessage處理程式。最後,我們使用window.postMessage方法將MessageChannel.port2傳輸到 IFrame。

讓我們更詳細地研究一下iframe.contentWindow.postMessage行的工作原理。它接受三個引數

  1. 要傳送的訊息。對於這個初始的埠傳輸,訊息可以是一個空字串,但在本例中它被設定為'init'
  2. 訊息要傳送到的源。*表示“任何源”。
  3. 一個物件,其所有權被轉移到接收瀏覽上下文。在這種情況下,我們將MessageChannel.port2傳輸到 IFrame,以便它可以使用它與主頁面進行通訊。

當我們的按鈕被點選時,我們阻止表單正常提交,然後透過MessageChannel將我們在文字輸入框中輸入的值傳送到 IFrame。

在 IFrame 中接收埠和訊息

<iframe>元素中,我們有以下 JavaScript

js
const list = document.querySelector("ul");
let port2;

// Listen for the initial port transfer message
window.addEventListener("message", initPort);

// Setup the transferred port
function initPort(e) {
  port2 = e.ports[0];
  port2.onmessage = onMessage;
}

// Handle messages received on port2
function onMessage(e) {
  const listItem = document.createElement("li");
  listItem.textContent = e.data;
  list.appendChild(listItem);
  port2.postMessage(`Message received by IFrame: "${e.data}"`);
}

當透過window.postMessage方法從主頁面接收到初始訊息時,我們執行initPort函式。此函式儲存傳輸的埠並註冊一個onmessage處理程式,該處理程式將在每次透過我們的MessageChannel傳遞訊息時被呼叫。

當收到來自主頁面的訊息時,我們建立一個列表項並將其插入到無序列表中,將列表項的textContent設定為事件的data屬性(其中包含實際訊息)。

接下來,我們透過呼叫最初傳輸到 iframe 的MessageChannel.port2上的MessagePort.postMessage,將一條確認訊息透過訊息通道傳送回主頁面。

在主頁面中接收確認

回到主頁面,現在讓我們看一下onmessage處理程式函式。

js
// Handle messages received on port1
function onMessage(e) {
  output.innerHTML = e.data;
  input.value = "";
}

當從 IFrame 收到確認原始訊息已成功接收的訊息時,這會將確認輸出到段落,並清空文字輸入框,以便傳送下一條訊息。

規範

規範
HTML
# message-channels
HTML
# message-ports

瀏覽器相容性

api.MessageChannel

api.MessagePort

另見