我已经完成了从background.js向content.js发送一条消息,使用了 chrome.runtime.onMessage.addListener
但我需要在整个扩展的生命周期中发送大量的消息,因此我需要打开一个端口,使用 长信.
然而,我找不到任何打开端口并在 background.js 文件和 content.js 文件之间发送 messagesobjects 的工作实例。有没有人有一个像样的例子可以用?
在尝试官方文档中的例子时,我也遇到了以下问题 "Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."
如果有任何帮助,在background.js和content.js文件之间提供一个现代工作的 "长寿消息 "的例子,我将非常感激 :D。
按照要求,这是我目前使用的代码,可以用于单条消息发送。我只是想改变这一点,使我可以发送很多消息,而不是只发送一条消息。
// Content.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message == 'dataready') {
//Do something
sendResponse({});
return true;
}
}
);
// Background.js
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {
message: 'dataready',
action: dataForiFrame
},
function(response) {
});
});
}
});
除非你每秒发送数百条消息,否则可能不需要使用基于端口的消息传递,但让我们看看它可能会是什么样子。
你的设置相对复杂,所以你需要维护一个 Map
的tabId到 port
关系。
内容脚本会启动chrome.runtime.connect,后台脚本会在onConnect中监听并更新映射。然后tabs.onUpdated监听器会使用这个映射将消息发送到正确的端口。
后台脚本:内容脚本。
const port = chrome.runtime.connect({name: 'content'});
port.onMessage.addListener(msg => {
console.log(msg.data);
// send a response if needed, may be a simple object/array
port.postMessage({id: msg.id, data: 'gotcha'});
});
后台脚本:
const portMap = new Map();
const resolveMap = new Map();
let messageId = 0;
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete') {
const response = await send(tabId, {message: 'dataready', action: 'foo'});
console.log(response);
}
});
chrome.runtime.onConnect.addListener(port => {
portMap.set(port.sender.tab.id, port);
port.onDisconnect.addListener(onPortDisconnected);
port.onMessage.addListener(onPortMessage);
});
function onPortDisconnected(port) {
portMap.delete(port.sender.tab.id);
}
function onPortMessage(msg, port) {
resolveMap.get(msg.id)(msg.data);
resolveMap.delete(msg.id);
}
function send(tabId, data) {
return new Promise(resolve => {
const id = ++messageId;
resolveMap.set(id, resolve);
portMap.get(tabId).postMessage({id, data});
});
}
这是一个非常赤裸裸的例子,没有进行任何错误检查。另外请注意,这不是唯一或最好的解决方案。根据问题中不存在的其他因素,可能还有其他的解决方案。
例如,可以反过来握手,在后台脚本中调用chrome.tabs.connect来连接内容脚本里面的onConnect。
内容脚本:内容脚本。
chrome.runtime.onConnect.addListener(port => {
port.onMessage.addListener(msg => {
console.log(msg.data);
// send a response if needed, may be a simple object/array
port.postMessage({id: msg.id, data: 'gotcha'});
});
});
后台脚本:
const portMap = new Map();
const resolveMap = new Map();
let messageId = 0;
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete') {
const response = await send(tabId, {message: 'dataready', action: 'foo'});
console.log(response);
}
});
function onPortDisconnected(port) {
portMap.delete(port.sender.tab.id);
}
function onPortMessage(msg, port) {
resolveMap.get(msg.id)(msg.data);
resolveMap.delete(msg.id);
}
function send(tabId, data) {
return new Promise(resolve => {
const id = ++messageId;
let port = portMap.get(tabId);
if (!port) {
port = chrome.tabs.connect(tabId, {frameId: 0});
port.onDisconnect.addListener(onPortDisconnected);
port.onMessage.addListener(onPortMessage);
portMap.set(tabId, port);
}
resolveMap.set(id, resolve);
port.postMessage({id, data});
});
}
P.S. 例子的问题在于人们往往在不理解其原理的情况下照搬。例如,你的代码似乎不必要地在onUpdated监听器中使用chrome.tabs.query,尽管它已经有了 tabId
参数,从而假设总是活动标签页被更新,即使不是这样。