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

React Chrome扩展中chrome.storage未定义的问题排查与解决

问题描述

我正在开发一款Chrome扩展,用来把React应用渲染嵌入到当前网页中。已经在manifest.json配置了权限:

// manifest.json    
"permissions": [
    "storage",
    "activeTab"
  ],

通过content.ts将应用嵌入网页:

// contents.ts
if (!document.getElementById("mysecretid")) {
    const div = document.createElement("div");
    div.id = "mysecretid";
    document.body.appendChild(div);

    const script = document.createElement("script");
    script.src = chrome.runtime.getURL("controlPanel.js");

    document.body.parentElement.appendChild(script);
}

尝试用use-chrome-storage npm包实现本地存储,控制面板组件代码如下:

import { useChromeStorageLocal } from "use-chrome-storage";
import { useState } from 'react';

const ControlPanel = (): JSX.Element => {
    const [render, setRender] = useState(true);
    const locale = "en";

    const [value] = useChromeStorageLocal("counterLocal", 0);

    console.log("value is", value);

    return (
        <div>Some components...</div>
    );
};

但渲染时崩溃,报错:

Cannot read properties of undefined (reading 'onChanged')

对应压缩代码行:

return chrome.storage.onChanged.addListener(t),

请问为何chrome.storage在此场景下未定义?该如何解决?

原因分析
  • 你通过document.createElement("script")插入的controlPanel.js运行在**页面的主上下文(main world)**中,这个环境和Chrome扩展的隔离上下文完全分离,没有权限访问chrome.storage这类扩展专属API。
  • 只有扩展的content script(直接在manifest里声明的、或通过chrome.scripting.executeScript注入的隔离脚本)才能访问扩展API,页面上下文无法获取chrome对象。
解决方案

推荐两种可行思路,优先选择思路一:

思路一:让React应用运行在隔离的content script上下文

  1. 修改打包配置,将React应用打包为content script兼容的模块。
  2. 在content.ts里直接渲染React组件,不再插入脚本到页面:
// content.ts
import React from 'react';
import ReactDOM from 'react-dom/client';
import ControlPanel from './ControlPanel';

if (!document.getElementById("mysecretid")) {
    const div = document.createElement("div");
    div.id = "mysecretid";
    document.body.appendChild(div);

    const root = ReactDOM.createRoot(div);
    root.render(<ControlPanel />);
}
  1. 确保manifest.json中正确声明content script的匹配规则,打包后的文件需加入web_accessible_resources(若有资源访问需求)。这种方式下,组件可直接调用chrome.storage或use-chrome-storage包。

思路二:通过消息通信跨上下文交互

如果必须将React应用插入到页面上下文,可通过chrome.runtime.sendMessage与扩展的background/content script通信,由扩展侧处理存储操作:

  1. 在background script中监听消息,处理存储读写:
// background.js
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
    if (msg.type === 'GET_STORAGE') {
        chrome.storage.local.get(msg.key, (result) => {
            sendResponse(result[msg.key] ?? msg.defaultValue);
        });
        return true; // 异步响应需返回true
    } else if (msg.type === 'SET_STORAGE') {
        chrome.storage.local.set({[msg.key]: msg.value}, () => {
            sendResponse(true);
        });
        return true;
    }
});
  1. 在React组件中封装自定义存储hooks,替换use-chrome-storage:
import { useState, useEffect } from 'react';

const useChromeStorageLocal = (key: string, defaultValue: any) => {
    const [value, setValue] = useState(defaultValue);

    useEffect(() => {
        // 读取初始值
        chrome.runtime.sendMessage({
            type: 'GET_STORAGE',
            key,
            defaultValue
        }, (res) => {
            setValue(res);
        });

        // 监听存储变化(需content script转发事件)
        const handleStorageChange = (e: CustomEvent) => {
            const { changes } = e.detail;
            if (changes[key]) {
                setValue(changes[key].newValue);
            }
        };
        window.addEventListener('storageChanged', handleStorageChange as EventListener);

        return () => window.removeEventListener('storageChanged', handleStorageChange as EventListener);
    }, [key, defaultValue]);

    const setStorageValue = (newValue: any) => {
        chrome.runtime.sendMessage({
            type: 'SET_STORAGE',
            key,
            value: newValue
        }, () => {
            setValue(newValue);
        });
    };

    return [value, setStorageValue];
};

const ControlPanel = (): JSX.Element => {
    const [render, setRender] = useState(true);
    const locale = "en";

    const [value] = useChromeStorageLocal("counterLocal", 0);

    console.log("value is", value);

    return (
        <div>Some components...</div>
    );
};
  1. 在content script中监听chrome.storage.onChanged事件,转发给页面上下文:
// content.ts
chrome.storage.onChanged.addListener((changes) => {
    const event = new CustomEvent('storageChanged', { detail: { changes } });
    window.dispatchEvent(event);
});

// 保留原插入脚本的代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 20:47:26