(第四章)浏览器插件开发(附源码):Content Script(内容脚本) 与插件其他部分的通信

184 阅读3分钟

(第四章)浏览器插件开发(附源码):Content Script(内容脚本) 与插件其他部分的通信

Content Script(内容脚本) 的通信方式

由于内容脚本运行在网页中,而不是在浏览器插件的环境,当内容脚本需要和插件其他部分交换信息时就涉及到通信

通信的方式包括用于一次性请求的简单通信和用于长期连接的长连接通信。

一次性请求通信

情形一:插件其他部分发送一次性消息到 Content Script(内容脚本)

插件其他部分利用chrome.tabs.sendMessage来发送消息。

但是在向内容脚本发送消息之前,内容脚本可能会生效在多个 tab 页面,我们需要知道向哪个 tab 页的内容脚本发送消息。我们先需要利用chrome.tabs.query来获取并筛选标签页,找到我们的目标标签页之后再向内容脚本发送消息。

// popup/popup.js
// 获取当前活动的标签页
// active: true表示标签页处于活动状态,
// currentWindow: true表示标签页位于当前窗口中
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
// 向content_script发送消息
const response = await chrome.tabs.sendMessage(tab.id, { color: "red" });
console.log(response);
Content Script 接收插件其他部分发来的一次性消息

我们可以利用chrome.runtime.onMessage来接收消息。(接收一次性消息时,插件其他部分和内容脚本都是用该api进行消息接收)

// scripts/content.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  sendResponse({ message: `已接受到popup的消息,颜色为${request.color}` });
  // 设置背景色
  setBgColor(request.color);
});

const setBgColor = (color) => {
  document.body.style.backgroundColor = color;
};
情形二:Content Script(内容脚本) 中发送一次性信息到插件其他部分
// scripts/content.js
const response = await chrome.runtime.sendMessage({ message: "popup你好" });
console.log(response);

需要注意的是:sendResponse 是同步调用的,如果想要在异步调用需要在 onMessage 的事件处理函数中添加 return true;

// scripts/content.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  setTimeout(() => {
    sendResponse({
      message: `已接受到消息,并完成设置背景色颜色为${request.color}`,
    });
    setBgColor(request.color);
  }, 2000);
  return true;
});

const setBgColor = (color) => {
  document.body.style.backgroundColor = color;
};

长期连接通信

情形一:在 Content Script(内容脚本) 中主动发起长期连接通信

在 Content Script(内容脚本) 中,我们可以利用chrome.runtime.connect()来建立连接。 建立连接时,每个端都会分配一个 runtime.Port 对象,以便通过该连接发送接收消息。

// scripts/content.js
const port = chrome.runtime.connect({ name: "建立与popup的连接" }); //建立连接
port.postMessage({ answer: "popup你好" }); //发送消息

// 通过port.onMessage.addListener接收消息
port.onMessage.addListener(function (msg) {
  if (msg.question === "你是坤坤吗?")
    port.postMessage({ answer: "我是content_script" });
  else if (msg.question === "别来烦我")
    port.postMessage({ answer: "好的,再见" });
});
情形二:在插件其他部分中主动发起长期连接通信

在插件其他部分利用chrome.runtime.onConnect()来建立连接。

// popup/popup.js
const port = chrome.tabs.connect({ name: "建立与坤坤的连接" }); //建立连接
port.postMessage({ question: "坤坤你在哪?" }); //发送消息
// 通过port.onMessage.addListener接收消息
port.onMessage.addListener(function (msg) {
  if (msg.answer === "popup你好")
    port.postMessage({ question: "你是坤坤吗?" });
  else if (msg.answer === "我是content_script")
    port.postMessage({ question: "别来烦我" });
  else if (msg.answer === "好的,再见")
    port.postMessage({ question: "坤坤你在哪?" });
});
接收长期连接的消息

如果仅仅只是接受长连接的消息,则无需创建连接,直接使用chrome.runtime.onConnect.addListener()来监听信息即可

// #长通信
chrome.runtime.onConnect.addListener(function(port) {
    port.onMessage.addListener(function(msg) {
        console.log(`content_script收到的消息:${msg}`);
        if (msg.question === "坤坤你在哪?")
          port.postMessage({answer: "你好"});
        else if (msg.question === "你是坤坤吗?")
          port.postMessage({answer: "我是content_script"});
        else if (msg.question === "我找坤坤, 坤坤你在哪?")
          port.postMessage({answer: "他跳舞去了"});
      });
  });

源码