告诉后台脚本何时将内容脚本注入React?

时间:2019-02-13 11:20:11

标签: javascript reactjs google-chrome-extension

我正在将React组件作为脚本注入到网页中,但这是在通过后台脚本打开的页面上。

我要做的是等到页面加载完毕并安装React组件,然后向该组件发送一条消息。不幸的是,我无法正常工作,所以我怀疑是因为Chrome表示页面已加载,但React组件尚未安装。

在我的后台脚本中,我有以下内容:

function relist(request) {
    const url = 'domain.com';
    chrome.tabs.create({ url },() => {
        chrome.tabs.onUpdated.addListener(reListener);
    });
    // Listen for new relist tab and remove listener when loaded
    function reListener(tabId, info, tab) {
        if(tab.url === url && tab.status === "complete") {
            console.log("Found tab");
            chrome.tabs.sendMessage(tab.id, {action: "relist", data: request.data});
            chrome.tabs.onUpdated.removeListener(reListener);
        }
    }
}

然后在组件中,我要注入以下内容:

componentDidMount() {
    console.log("Mounted Design Upload");
    chrome.runtime.onMessage.addListener(
        (request) => {
            console.log("Received Message");
            if(request.action === "relist") {
                console.log("relist", request);
                this.props.dispatch(currentDesign({
                    id: request.data.design_slug,
                    meta:request.data.meta,
                }));
                this.setState({
                    value: {
                        key: request.data.design_slug,
                        name: request.data.design.title
                    },
                    selectedFile: request.data.file_slug
                })
            }
        });
}

有人知道我如何告诉后台脚本在发送数据之前等待组件发出的消息吗?

1 个答案:

答案 0 :(得分:1)

在内容脚本和后台脚本之间建立长期连接。

想象一下这样的文件夹结构:

├── content
├───── AwesomeComponent.js
├── background
├───── index.js
├── messenger.js
├── manifest.json

您将拥有一个脚本,该脚本可以从内容脚本和后台脚本发送/接收消息。

messenger.js

import { relist } from 'background/index'

chrome.runtime.onConnect.addListener(function (port) {
  port.onMessage.addListener(function (request) {
    if (request.action === 'mounted') {
       // this message was sent by your AwesomeComponent
       // call relist to send data to content script
       relist()
    }

    if (request.action === 'relist') {
       // this message was sent by your background script
       // dispatch an action with request.data using your state management library
       // this will trigger an re-render in your React app and update its props
    }
  })
})

const context = typeof chrome.runtime.getBackgroundPage !== 'function' ? 'content' : 'background'

export function postMessage (request) {
  if (context === 'content') {
    const port = browser.runtime.connect()
    port.postMessage(request)
  } else {
    const port = browser.tabs.connect(request.tabId)
    port.postMessage(request)
  }
}

messenger.js中包含content/index.js模块。这将添加一个chrome.runtime.onConnect侦听器。

content/AwesomeComponent.js

import React from 'react'
import { render } from 'react-dom'

import messenger from '../messenger'

class AwesomeComponent extends React.Component {
  // [...]

  componentDidUpdate(prevProps) {
    /* 
      Component was updated because a dispatch action was fired 
      through a message coming from the background script.
      Compare previous props and then use setState.
      You SHOULD compare them or it'll cause an infinite rendering loop!
      A comparison may be...
    */
    if (this.props.value.key !== prevProps.value.key) {
      this.setState({
        value: {
          key: props.design_slug,
          name: props.design.title
        },
        selectedFile: props.file_slug
      })
    }
  }

  componentDidMount () {
    // send a message to background
    messenger.postMessage({
       action: 'mounted'
    })
  }

  // [...]
}

还将messenger.js模块包括在后台脚本index.js中。这还将添加一个chrome.runtime.onConnect侦听器。

background/index.js

import messenger from '../messenger'

export function relist (data) {
  const data = ...
  chrome.tabs.create({ url: 'domain.com' }, () => {
    chrome.tabs.onUpdated.addListener(function reListener(tabId, info, tab) {
      if(tab.url === url && tab.status === 'complete') {
        console.log('Found tab');
        messenger.postMessage({
          tabId: tab.id,
          action: 'relist', 
          data: data
        })
        chrome.tabs.onUpdated.removeListener(reListener);
      }
   }
}

// other background stuff