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