Проблема со связью между страницей пользовательского интерфейса расширения Chrome и скриптом содержимого

#reactjs #webpack #google-chrome-extension

#reactjs #webpack #google-chrome-extension

Вопрос:

Здесь вопрос новичка:

На прошлой неделе я написал очень простое расширение Chrome, в котором popup.js отправляет сообщение на content.js сценарий, следующим образом (возможно, это был не лучший способ, но здесь важно то, что он сработал. Почему это важно? Потерпите меня …)

В popup.js

 someButton.addEventListener('click', (event) => {
    chrome.tabs.executeScript(null, {
        file: 'src/content.js',
    }, () => {
        connect();
    });
});
  

и более поздние версии:

 function connect() {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        const port = chrome.tabs.connect(tabs[0].id);
        port.postMessage({ type: 'info', data: someValue });
    })
}

  

В content.js

 if (!chrome.runtime.onConnect.hasListeners()) {
    chrome.runtime.onConnect.addListener((port) => {
        port.onMessage.addListener((msg) => {
            if (msg amp;amp; msg.type == "info") {
               // do some stuff
            }
        });
    });
}
  

Это сработало, но я считаю, что это не лучший способ. Я читал гораздо больше с прошлой недели и знаю, что считаю, что правильный путь должен быть примерно таким:

На popup.js

 chrome.tabs.query({ active: true, currentWindow: true },  (tabs) => {
      chrome.tabs.sendMessage(tabs[0].id, {
        type: "info",
        someValue: value
      });
    });
  

И в content.js

 chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
        if (request.type == "info"){
           sendResponse({ message: "some answer" }) 
        }
        return true;
    })

  

Но в моей новой версии это не работает, даже если в каждом руководстве и документации указано, что это должно. Я не получаю никаких ошибок, просто функция, добавленная в прослушиватель, никогда не выполняется.

Я подозреваю, что, возможно, это может быть связано с тем, что сейчас я использую React и Webpack, и, возможно, Webpack делает что-то странное?

Фрагмент моего manifest.json

  "background": {
        "persistent": false,
        "scripts": ["background.bundle.js"]
    },
    "content_scripts": [{
        "matches": ["<all_urls>"],
        "js": ["content.bundle.js"]
    }],
  

Может быть, проблема в том, что я указываю содержимое и фон с помощью пакетов? Не уверен, единственное, что я знаю, это то, что, насколько я понимаю, это должно работать, а это не так.

Чего мне не хватает?

PS: Я должен добавить, что я проводил отладку и обнаружил, что файлы содержимого выполняются, и фрагмент кода, который отправляет сообщение, также был выполнен. Похоже, что addListener не добавлен должным образом или по какой-то причине никогда не получал сообщение.

Ответ №1:

Когда вы объявляете content_scripts в manifest.json, по умолчанию он выполняется между событиями DOMContentLoaded и load , поэтому, если вы открыли всплывающее окно на сложной странице, которая все еще загружается, сценарий содержимого для получения сообщения не будет.

Вы можете указать «run_at»: «document_start», чтобы скрипт содержимого запускался при «запуске», когда страница все еще пуста, в DOM будет только document.documentElement для <html> узла, не будет <head> или <body> .

Обратите внимание, что сценарий содержимого, объявленный в manifest.json, всегда запускается на каждой соответствующей странице, поэтому, если вашему расширению требуется сценарий содержимого только после щелчка во всплывающем окне, это будет очень, очень плохо, потому что вы тратите ресурсы 99,999% времени и фактически требуете широких разрешений хоста. В этом случае вам следует 1) удалить content_scripts из manifest.json, 2) добавить activeTab в «разрешения» и удалить все широкие разрешения хоста, такие как <all_urls> или *://*/* или http://*/* , 3) использовать executeScript и hasListeners, как вы делали раньше.

Комментарии:

1. большое вам спасибо, как всегда, ваш ответ очень проясняет. Но, к сожалению, у меня все еще есть некоторые сомнения. Например, поскольку я использовал executeScript, я отправил имя файла, но с webpack должен использовать имя пакета? Что-то вроде chrome.tabs.executeScript( null, { file: "content.bundle.js", }, () => { connect(); } );

2. Извините за форматирование кода, комментарии обладают меньшей гибкостью для этого

3. Не могу помочь с webpack. Это зависит от конфигурации вашего webpack, поэтому попробуйте задать новый вопрос об этом.