使用react构建的Chrome扩展,chrome.runtime.sendMessage的响应未定义并且接收端不存在

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

每当我第一次加载扩展程序(我在开发模式下执行)或重新加载扩展程序时,我都会在background.js中得到未定义的响应(检索HTML内容时出错:未定义)。 错误为,未检查的runtime.lastError:无法建立连接。接收端不存在。

我的文件夹结构是

刮刀 距离/ 民众/ 图片/ 索引.html 清单.json 源代码/ 扩展/ 背景.js 内容脚本.js 应用程序.tsx 实用程序/ chrome-api.js

应用程序.tsx

import React, { useEffect } from 'react';
import './App.css';
import { useRecoilState } from 'recoil';
import messagesState from './states/messages';
import { isValidChromeRuntime } from './utils/chrome-api';
function App() {
  const [htmlContent, setHtmlContent] = useRecoilState(messagesState);

  const scrapeTab = () => {
    if (chrome && chrome.runtime) {
      chrome.runtime.sendMessage({ action: 'getHTML' }, function (response) {
        console.log(response);
        if (response) {
          setHtmlContent(response);
        } else {
          console.error('Error retrieving HTML content:', response);
        }
      });
    } else {
      console.error(
        'Chrome API is not available. Make sure this component is running in a Chrome extension context.',
        chrome
      );
    }
  };
  useEffect(() => {
    if (isValidChromeRuntime()) {
      scrapeTab();
    } else {
      console.log('here');
    }
  }, []);
  return (
    <div className="App">
      <h1>Slack scraper</h1>
      {JSON.stringify(htmlContent)}
    </div>
  );
}

export default App;

背景.js

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
  if (request.action === 'getHTML') {
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
      if (tabs.length) {
        chrome.tabs.sendMessage(
          tabs[0].id,
          { action: 'getHTML' },
          function (response) {
            if (response) {
              sendResponse(response);
            } else {
              console.error('Error retrieving HTML content:', response);
            }
          }
        );
      }
    });
    return true;
  }
});

contentScript.js

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
  if (request.action === 'getHTML') {
    const messages = scrapeTheDoc();
    sendResponse(messages);
  }
});
const scrapeTheDoc = () => {
  const container = document.querySelector('.p-workspace__primary_view_body');
  return container.toString();
};
document.addEventListener('scroll', function () {
  chrome.runtime.sendMessage({ action: 'scrollEvent' });
});

manifest.json

{
  "manifest_version": 3,
  "name": "scraper",
  "version": "1.0",
  "description": "Scraper",
  "host_permissions": ["<all_urls>"],
  "icons": {
    "16": "images/logo.png",
    "32": "images/logo.png",
    "180": "images/logo.png",
    "192": "images/logo.png",
    "512": "images/logo.png"
  },
  "offline_enabled": true,
  "permissions": ["storage", "activeTab", "contextMenus", "tabs"],
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["contentScript.js"]
    }
  ],
  "action": {
    "default_popup": "index.html"
  }
}

chrome-api.js

export const isValidChromeRuntime = () => {
  return chrome.runtime && !!chrome.runtime.getManifest();
};

webpack.config.js

const HTMLPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('path');

module.exports = {
  entry: {
    background: './src/extensions/background.js',
    contentScript: './src/extensions/contentScript.js',
    popup: './src/index.tsx',
  },
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        use: 'babel-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.jsx'],
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  plugins: [
    new HTMLPlugin({
      template: './public/index.html',
      chunks: ['popup'],
      filename: 'index.html',
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: './public',
          to: '',
          globOptions: {
            ignore: ['**/index.html'],
          },
        },
      ],
    }),
  ],
};

javascript reactjs google-chrome-extension chrome-extension-manifest-v3 browser-extension
1个回答
0
投票

您对问题的分析表明对内容和后台脚本之间的通信流程存在误解。根据提供的代码片段,您似乎正在尝试从活动选项卡中抓取 HTML 内容,然后将其转发到后台脚本以进行进一步处理。

但是,您当前的方法存在一些不一致和低效的地方:

  1. 通信流程:您当前的设置涉及内容脚本向后台脚本发送消息以使用

    scrapeTheDoc
    提取 HTML,但后台脚本中不会进一步利用此 HTML 内容。

  2. 消息结构:内容脚本和后台脚本之间交换的消息格式似乎不一致。最初,您使用

    { action: 'getHTML' }
    来请求 HTML 内容,但随后您发送的 HTML 内容本身没有清晰的结构。

为了解决这些问题并改进扩展的功能,我建议采用以下方法:

  1. 内容脚本修改:抓取文档应该保留在内容脚本中,因为它直接与 DOM 交互。抓取 HTML 后,将其发送到带有适当操作标识符的后台脚本,例如

    { action: "content", data: HTMLScraped }

  2. 后台脚本处理:修改后台脚本来处理接收到的HTML内容,并根据需要广播到其他选项卡。确保消息结构一致且易于跨脚本解释。

通过细化通信流程和消息结构,您可以提高分机的可靠性和效率。虽然在没有完整项目的情况下很难进行诊断,但充分实施这些建议应该有助于解决您的具体问题。如果您遇到任何进一步的挑战或需要额外的帮助,请提供更多详细信息以获取进一步的指导。

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