Background.js到content.js使用端口&长久的消息传递。

问题描述 投票:0回答:1

我已经完成了从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) {

            });  
        });
    }
});
javascript google-chrome-extension
1个回答
1
投票

除非你每秒发送数百条消息,否则可能不需要使用基于端口的消息传递,但让我们看看它可能会是什么样子。

你的设置相对复杂,所以你需要维护一个 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 参数,从而假设总是活动标签页被更新,即使不是这样。

© www.soinside.com 2019 - 2024. All rights reserved.