简体   繁体   English

Background.js 到 content.js 使用端口和长寿命消息传递

[英]Background.js to content.js using Port & Long-lived messaging

I've accomplished sending a single message from background.js to content.js using chrome.runtime.onMessage.addListener but I need to send a lot of messages throughout the extensions lifecycle, therefore I need to open a port and use long-lived messaging .我已经使用chrome.runtime.onMessage.addListener完成了从 background.js 向 content.js 发送一条消息,但我需要在整个扩展生命周期中发送大量消息,因此我需要打开一个端口并使用long-lived消息传递

However, I can't find any working examples of opening a port and sending messages/objects between a background.js file and a content.js file.但是,我找不到任何在 background.js 文件和 content.js 文件之间打开端口和发送消息/对象的工作示例。 Does anyone have a decent example that works?有没有人有一个很好的例子?

I've also run into issues with the following when trying the example in the official docs "Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."在尝试官方文档"Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."

Any help with a modern working 'long-lived messaging' example between a background.js and content.js file would be much appreciated:D对于 background.js 和 content.js 文件之间的现代工作“长期消息传递”示例的任何帮助将不胜感激:D

As requested here is the code I'm currently using that works for single message sending.正如这里所要求的,我目前正在使用的代码适用于单条消息发送。 I'd just like to change this so that I can send lots of messages, not just one.我只是想改变这一点,这样我就可以发送很多消息,而不仅仅是一个。

// 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) {

            });  
        });
    }
});

Unless you send hundreds of messages per second there's probably no need to use port-based messaging but let's see how it might look.除非您每秒发送数百条消息,否则可能不需要使用基于端口的消息传递,但让我们看看它的外观。

Your setup is relatively complicated so you would need to maintain a Map of tabId to port relations.您的设置相对复杂,因此您需要维护Map的 Map 到port关系。

Handshake initiated in content script在内容脚本中发起的握手

The content script would initiate chrome.runtime.connect, the background script would listen in onConnect and update the mapping.内容脚本会启动 chrome.runtime.connect,后台脚本会监听 onConnect 并更新映射。 Then tabs.onUpdated listener will use this map to send the message to the right port.然后 tabs.onUpdated 监听器将使用这个 map 将消息发送到正确的端口。

content script:内容脚本:

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'}); 
});

background script:背景脚本:

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});
  });
}

This is a very barebone example without any error-checking.这是一个非常简单的示例,没有任何错误检查。 Also note, it's not the only or the best solution.另请注意,这不是唯一或最好的解决方案。 Depending on other factors that aren't present in the question there could be other solutions.根据问题中不存在的其他因素,可能会有其他解决方案。


Handshake initiated in background script在后台脚本中启动握手

For example, it's possible to reverse the handshake and call chrome.tabs.connect in the background script to connect to onConnect inside the content script.例如,可以反转握手并在后台脚本中调用 chrome.tabs.connect 以连接到内容脚本中的 onConnect。

content script:内容脚本:

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'}); 
  });
});

background script:背景脚本:

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});
  });
}

PS The problem with examples is that people tend to copy them without understanding the mechanics. PS 示例的问题在于人们倾向于在不了解机制的情况下复制它们。 For instance, your code seems to needlessly use chrome.tabs.query inside onUpdated listener even though it already has tabId parameter, thus assuming it's always the active tab that got updated even when it's not so.例如,您的代码似乎在 onUpdated 侦听器中不必要地使用了 chrome.tabs.query,即使它已经具有tabId参数,因此假设它始终是更新的活动选项卡,即使它不是这样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM