runtime.Port

Port 物件表示兩個特定上下文之間連線的一端,可用於交換訊息。

一方使用 connect() API 發起連線。這將返回一個 Port 物件。另一方使用 onConnect 監聽器偵聽連線嘗試。它會收到一個相應的 Port 物件。

一旦雙方都擁有 Port 物件,它們就可以使用 Port.postMessage()Port.onMessage 交換訊息。當它們完成時,任一端都可以使用 Port.disconnect() 斷開連線,這將在另一端生成 Port.onDisconnect 事件,使另一端能夠執行任何必需的清理工作。

Port 也可能響應各種事件斷開連線。請參閱 生命週期

您可以使用此模式在

您需要為不同型別的連線使用不同的連線 API,如下表所示。

連線型別 發起連線嘗試 處理連線嘗試
後臺指令碼到內容指令碼 tabs.connect() runtime.onConnect
內容指令碼到後臺指令碼 runtime.connect() runtime.onConnect
擴充套件到原生應用程式 runtime.connectNative() 不適用(請參閱 原生訊息)。
擴充套件到擴充套件 runtime.connect() runtime.onConnectExternal

型別

此型別的值是物件。它們包含以下屬性:

name

string。埠的名稱,由建立它的 runtime.connect()tabs.connect() 呼叫定義。如果此埠連線到原生應用程式,則其名稱是原生應用程式的名稱。

disconnect

function。斷開埠連線。任一端在完成埠使用後都可以呼叫此函式。它將導致在另一端觸發 onDisconnect。如果您希望另一端維護與此埠相關的某些狀態,可以在斷開連線時進行清理,則此功能非常有用。如果此埠連線到原生應用程式,此函式將關閉原生應用程式。

error

object。如果埠因錯誤而斷開連線,則此欄位將設定為一個具有 message 字串屬性的物件,為您提供有關錯誤的更多資訊。請參閱 onDisconnect

onDisconnect

object。它包含所有使用 WebExtension API 構建的擴充套件的事件通用的 addListener()removeListener() 函式。當另一端呼叫 Port.disconnect() 時,將呼叫偵聽器函式。每個埠只會觸發一次此事件。偵聽器函式將收到 Port 物件。如果埠因錯誤而斷開連線,則 Port 引數將包含一個 error 屬性,提供有關錯誤的更多資訊。

js
port.onDisconnect.addListener((p) => {
  if (p.error) {
    console.log(`Disconnected due to an error: ${p.error.message}`);
  }
});

請注意,在 Google Chrome 中不支援 port.error:而是使用 runtime.lastError 來獲取錯誤訊息。

onMessage

object。它包含所有使用 WebExtension API 構建的擴充套件的事件通用的 addListener()removeListener() 函式。當另一端向此埠傳送訊息時,將呼叫偵聽器函式。偵聽器將收到另一端傳送的值。

postMessage

function。向另一端傳送訊息。此函式接受一個引數,該引數是一個可序列化的值(請參閱 資料克隆演算法),代表要傳送的訊息。該訊息將傳遞給偵聽埠 onMessage 事件的任何指令碼,或者如果此埠連線到原生應用程式,則傳遞給原生應用程式。

sender 可選

runtime.MessageSender。包含有關訊息傳送者的資訊。僅在傳遞給 runtime.onConnectruntime.onConnectExternalruntime.onUserScriptConnect 偵聽器的埠上存在。

生命週期

Port 的生命週期在 Chrome 文件中有描述。

然而,Firefox 和 Chrome 之間有一個重要的區別,源於 runtime.connecttabs.connect API 是廣播頻道。這意味著可能存在多個接收者,當其中一個具有 runtime.onConnect 呼叫的上下文關閉時,這會導致歧義。在 Chrome 中,只要有任何其他接收者,埠就會保持活動狀態。在 Firefox 中,當任何一個上下文解除安裝時,埠就會關閉。換句話說,斷開連線條件,

  • 接收到該埠的所有框架(透過 runtime.onConnect)都已解除安裝。

在 Chrome 中成立,在 Firefox 中被替換為

  • 接收到該埠的*任何*框架(透過 runtime.onConnect)已解除安裝。

(請參閱 bug 1465514)。

示例

從內容指令碼連線

此內容指令碼

  • 連線到後臺指令碼,並將 Port 儲存在名為 myPort 的變數中。
  • 偵聽 myPort 上的訊息並將其記錄下來。
  • 當用戶單擊文件時,使用 myPort 向後臺指令碼傳送訊息。
js
// content-script.js

let myPort = browser.runtime.connect({ name: "port-from-cs" });
myPort.postMessage({ greeting: "hello from content script" });

myPort.onMessage.addListener((m) => {
  console.log("In content script, received message from background script: ");
  console.log(m.greeting);
});

document.body.addEventListener("click", () => {
  myPort.postMessage({ greeting: "they clicked the page!" });
});

相應的後臺指令碼

  • 偵聽來自內容指令碼的連線嘗試。

  • 當收到連線嘗試時

    • 將埠儲存在名為 portFromCS 的變數中。
    • 使用該埠向內容指令碼傳送訊息。
    • 開始偵聽透過該埠接收的訊息,並將其記錄下來。
  • 當用戶單擊擴充套件的瀏覽器操作圖示時,使用 portFromCS 向內容指令碼傳送訊息。

js
// background-script.js

let portFromCS;

function connected(p) {
  portFromCS = p;
  portFromCS.postMessage({ greeting: "hi there content script!" });
  portFromCS.onMessage.addListener((m) => {
    console.log("In background script, received message from content script");
    console.log(m.greeting);
  });
}

browser.runtime.onConnect.addListener(connected);

browser.browserAction.onClicked.addListener(() => {
  portFromCS.postMessage({ greeting: "they clicked the button!" });
});

多個內容指令碼

如果您有多個內容指令碼同時通訊,您可能需要將每個連線儲存在陣列中。

js
// background-script.js

let ports = [];

function connected(p) {
  ports[p.sender.tab.id] = p;
  // …
}

browser.runtime.onConnect.addListener(connected);

browser.browserAction.onClicked.addListener(() => {
  ports.forEach((p) => {
    p.postMessage({ greeting: "they clicked the button!" });
  });
});

連線到原生應用程式

此示例連線到名為“ping_pong”的原生應用程式,並開始偵聽來自它的訊息。它還在使用者單擊瀏覽器操作圖示時向原生應用程式傳送訊息。

js
/*
On startup, connect to the "ping_pong" app.
*/
let port = browser.runtime.connectNative("ping_pong");

/*
Listen for messages from the app.
*/
port.onMessage.addListener((response) => {
  console.log(`Received: ${response}`);
});

/*
On a click on the browser action, send the app a message.
*/
browser.browserAction.onClicked.addListener(() => {
  console.log("Sending:  ping");
  port.postMessage("ping");
});

瀏覽器相容性

注意:此 API 基於 Chromium 的 chrome.runtime API。本文件摘自 Chromium 程式碼中的 runtime.json