You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过default_popup与长期运行的React Chrome扩展通信?

Hey there! Building a React-based Chrome Extension with a persistent overlay is totally doable—let's break down how to tackle your key requirements step by step.

Core Architecture Overview

Since the extension's popup is a short-lived page (it closes when you click away), we need three main parts to make this work:

  1. A content script that runs persistently in the active tab and manages the React overlay.
  2. A React popup that sends commands to toggle the overlay.
  3. chrome.storage.local to persist the overlay state (so it stays active after the popup closes, even if you refresh the tab).

Step 1: Project Structure & Build Setup

I recommend using Vite for this (it's simpler than Create React App for extension development). Your project structure will look like this:

src/
├── contentScript.js   # Injects React overlay and handles messaging
├── overlay.jsx        # Your React overlay component
├── popup.jsx          # The extension's popup UI
├── popup.html         # Entry point for the popup
├── background.js      # Optional (for cross-tab state management)
vite.config.js         # Build configuration
manifest.json          # Extension manifest

Configure Vite to bundle your files correctly with vite.config.js:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: {
        popup: './src/popup.html',
        overlay: './src/overlay.jsx',
        contentScript: './src/contentScript.js',
        background: './src/background.js'
      },
      output: {
        entryFileNames: '[name].bundle.js',
        chunkFileNames: '[name].chunk.js',
        assetFileNames: '[name].[ext]'
      }
    }
  }
});

Step 2: Manifest Configuration (v3)

Your manifest.json needs permissions for scripting, storage, and web access to your React bundle:

{
  "manifest_version": 3,
  "name": "React Overlay Extension",
  "version": "1.0",
  "action": {
    "default_popup": "popup.html"
  },
  "background": {
    "service_worker": "background.bundle.js"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["contentScript.bundle.js"],
      "run_at": "document_end"
    }
  ],
  "permissions": ["activeTab", "storage", "scripting"],
  "web_accessible_resources": [
    {
      "resources": ["overlay.bundle.js"],
      "matches": ["<all_urls>"]
    }
  ]
}

Step 3: Content Script (Manages Overlay)

This script runs in every tab, checks the saved state on load, and listens for toggle commands from the popup. It injects the React overlay into the page's main DOM:

// contentScript.js
function injectOverlay() {
  // Create a container for the React app
  const overlayContainer = document.createElement('div');
  overlayContainer.id = 'react-overlay-container';
  document.body.appendChild(overlayContainer);

  // Inject the React bundle into the main page context
  const script = document.createElement('script');
  script.src = chrome.runtime.getURL('overlay.bundle.js');
  script.type = 'module';
  document.body.appendChild(script);
}

// Load saved state when the tab loads
chrome.storage.local.get('overlayActive', (result) => {
  if (result.overlayActive) {
    injectOverlay();
  }
});

// Listen for toggle messages from the popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'TOGGLE_OVERLAY') {
    const container = document.getElementById('react-overlay-container');
    
    if (container) {
      // Remove overlay and update state
      container.remove();
      chrome.storage.local.set({ overlayActive: false });
    } else {
      // Add overlay and update state
      injectOverlay();
      chrome.storage.local.set({ overlayActive: true });
    }
    
    sendResponse({ success: true });
  }
});

Step 4: React Overlay Component

This is your full-screen overlay that stays active even after the popup closes. It can also send messages back to the content script to toggle itself:

// overlay.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';

const Overlay = () => {
  const closeOverlay = () => {
    // Send toggle command back to content script
    chrome.runtime.sendMessage({ type: 'TOGGLE_OVERLAY' });
  };

  return (
    <div style={{
      position: 'fixed',
      top: 0,
      left: 0,
      width: '100vw',
      height: '100vh',
      backgroundColor: 'rgba(0, 0, 0, 0.7)',
      zIndex: 999999,
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center'
    }}>
      <div style={{
        backgroundColor: 'white',
        padding: '2rem',
        borderRadius: '8px',
        boxShadow: '0 4px 12px rgba(0,0,0,0.2)'
      }}>
        <h2>My Persistent React Overlay</h2>
        <p>This stays visible even after the popup closes!</p>
        <button 
          onClick={closeOverlay}
          style={{ marginTop: '1rem', padding: '0.5rem 1rem', cursor: 'pointer' }}
        >
          Close Overlay
        </button>
      </div>
    </div>
  );
};

// Mount the React app to the injected container
const root = ReactDOM.createRoot(document.getElementById('react-overlay-container'));
root.render(<Overlay />);

Step 5: React Popup Component

The popup reads the current overlay state and sends toggle commands to the active tab's content script:

// popup.jsx
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom/client';

const Popup = () => {
  const [isOverlayActive, setIsOverlayActive] = useState(false);

  // Load current state from storage when popup opens
  useEffect(() => {
    chrome.storage.local.get('overlayActive', (result) => {
      setIsOverlayActive(result.overlayActive || false);
    });
  }, []);

  const toggleOverlay = () => {
    // Get the active tab and send toggle message
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      chrome.tabs.sendMessage(tabs[0].id, { type: 'TOGGLE_OVERLAY' }, (response) => {
        if (response?.success) {
          setIsOverlayActive(!isOverlayActive);
        }
      });
    });
  };

  return (
    <div style={{ padding: '1rem', width: '220px' }}>
      <h3>Overlay Controls</h3>
      <button 
        onClick={toggleOverlay}
        style={{ width: '100%', padding: '0.6rem', cursor: 'pointer', marginTop: '0.5rem' }}
      >
        {isOverlayActive ? 'Turn Off Overlay' : 'Turn On Overlay'}
      </button>
    </div>
  );
};

// Mount popup to its root element
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Popup />);

Key Notes

  1. Isolated World vs Main World: Content scripts run in an isolated context, so we inject the React bundle into the page's main world to render the overlay directly in the DOM.
  2. State Persistence: chrome.storage.local ensures the overlay state is saved across popup closes, tab refreshes, and even browser restarts.
  3. Messaging: We use Chrome's runtime messaging API (chrome.tabs.sendMessage and chrome.runtime.sendMessage) to communicate between the popup and content script.
  4. Background Service Worker: Optional, but you can use it to sync state across multiple tabs (e.g., if you want the overlay to toggle in all tabs at once).

Once you build the extension with npm run build, load the dist folder in Chrome's extension manager (enable developer mode first) and you're good to go!

内容的提问来源于stack exchange,提问作者agm1984

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:42:58