React功能组件中的Chrome API无法正常运行

时间:2020-10-29 04:12:48

标签: javascript reactjs google-chrome-extension create-react-app

我正在尝试进行chrome扩展,并希望使用React,但是经过一整周的死胡同之后,这似乎并不是理想的选择。因此,在只使用Vanilla JS之前,我决定向您寻求一些SO用户的建议,这是最后的努力。

这是我的public/manifest.json文件(我现在手动更改“ js”:[]字段以匹配构建文件夹的名称):

{
  "manifest_version": 2,
  "name": "Name",
  "version": "0.1.0",

  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["/static/js/main.356978b8.chunk.js"]
    }
  ],

  "background": {
    "scripts": ["background.js"]
  },

  "permissions": ["tabs", "http://*/"],

  "browser_action": {
    "default_title": "Title"
  }
}

我的想法是,内容脚本将是所有React函数组件文件以及使用以上内容的全部index.js

这里是package.json

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-scripts": "4.0.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "env": {
      "webextensions": true
    }
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

这是我的文件夹结构:

Folder Structure

src文件夹

// src/App.js
function App() {
  return <div>Hello</div>;
}

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  console.log(request.tabs);
});

export default App;

// src/index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

公用文件夹

// public/background.js
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.tabs.create(
    {
      windowId: null,
      url: "http://localhost:3000",
      active: true,
      openerTabId: tab.id,
    },
    (newTab) => {

      // wait for tab to load, then send message with tabs
      chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
        if (changeInfo.status == "complete" && tabId == newTab.id) {
          chrome.tabs.getAllInWindow(null, (tabs) => {
            chrome.tabs.sendMessage(newTab.id, { tabs });
          });
        }
      });
    }
  );
});

// public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>Title</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

当我用background.js单击扩展按钮时,我的http://localhost:3000只是向chrome.browserAction.onClicked.addListener((tab)=>{...})打开一个新标签。 这很好

在Chrome扩展程序中将build文件夹添加到Load Unpacked会生成扩展程序,但是当我单击按钮并创建新标签时,我得到了

TypeError: Cannot read property 'addListener' of undefined

我知道npm run eject可用于自动化/设置/static/js/...中的文件名,但是现在我可以在每次构建更改时只需对其进行更改即可。

任何线索或想法将不胜感激!

1 个答案:

答案 0 :(得分:1)

问题是您的扩展使用了在不同上下文中运行的同一React app JS文件("static/js/main.*.chunk.js" 两个 副本。一个副本作为内容脚本运行到活动页面中,并且该副本应无错误运行。但是,您在新标签页中打开的CRA开发服务器(localhost:3000)中还有另一个副本。而且该文件以普通 JS文件的形式运行,与您的扩展程序没有任何关系,因此它 不能 调用Chrome API。这就是为什么您会收到该错误。

一种可能的解决方案是将该JS文件分成两个单独的部分:一个(与index.html相关的普通JS)-仅执行React渲染,另一个(内容脚本)-侦听扩展名中的消息。这两个部分之间的Communication可以使用window.postMessage完成。在您的情况下,如下所示:

public / manifest.json:

{
...
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["contentScript.js"]
    }
  ],
...
}

public / contentScript.js:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  window.postMessage({ type: "FROM_EXT", tabs: request.tabs }, "http://localhost:3000");
});

src / index.js:

...
window.addEventListener("message", event => {
  if (event.source != window)
    return;
  const {type, tabs} = event.data;
  if (type !== "FROM_EXT")
    return;
  console.log(tabs);
});

此外,我建议您使用自定义CRA模板-complex-browserext(插件)。它有助于使用带有浏览器扩展的Create React App。有关特定用法示例(另一个插件),另请参见this article