Electron+React项目使用ipcRenderer存储数据时模块找不到报错求助
解决Electron + React中ipcRenderer引入导致的fs/path模块找不到问题
问题背景
开发跨端应用(PC端基于Electron,Web端用Firebase),PC端计划用electron-store做本地存储,在React组件中尝试引入ipcRenderer时触发模块找不到错误,项目未手动配置Webpack,使用react-scripts默认配置。
相关代码
package.json
{ "name": "app", "version": "0.0.1", "main": "public/electron.js", "dependencies": { "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", "concurrently": "^7.4.0", "cowsay": "^1.5.0", "cross-env": "^7.0.3", "electron": "^21.1.1", "electron-is-dev": "^2.0.0", "electron-store": "^8.1.0", "firebase": "^9.9.4", "jquery": "^3.6.1", "jquery-ui": "^1.13.2", "node-polyfill-webpack-plugin": "^2.0.1", "path-browserify": "^1.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", "react-use-draggable-scroll": "^0.4.7", "regexp": "^1.0.0", "typescript": "^4.8.4", "util": "^0.12.4", "uuid": "^9.0.0", "wait-on": "^6.0.1", "web-vitals": "^3.0.1" }, "scripts": { "react-start": "react-scripts start", "react-build": "react-scripts build", "react-test": "react-scripts test", "react-eject": "react-scripts eject", "electron-builder": "electron-builder", "build": "npm run react-build && npm run electron-build", "start": "concurrently \"cross-env BROWSER=none npm run react-start\" \"wait-on http://localhost:3000 && electron . --no-sandbox\"" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }
electron.js
const path = require("path"); const isDev = require("electron-is-dev"); const { FETCH_DATA_FROM_STORAGE, PUSH_DATA_TO_STORAGE } = require("../src/utils/Constants"); const electron = require('electron'); const app = electron.app; const ipcMain = electron.ipcMain; const BrowserWindow = electron.BrowserWindow; const Store = require('electron-store'); const createWindow = () => { const win = new BrowserWindow({ width: 1080, height: 720, }) win.loadURL( isDev ? 'http://localhost:3000' : "file://" + path.join(__dirname, "../build/index.html") ) } let store = new Store(); ipcMain.on('FETCH_DATA_FROM_STORAGE', (event, arg) => { console.log('store', store.get('contests')); }); app.whenReady().then(() => { createWindow() })
ContestHandlerApp.js
import { auth, firestore } from "../firebase/firebase-config"; import { Tools } from '../utils/Tools'; import { AbstractContestHandler } from "./AbstractContestHandler.ts"; import { FETCH_DATA_FROM_STORAGE, PUSH_DATA_TO_STORAGE } from "../utils/Constants"; const { ipcRenderer } = require('electron'); export class ContestHandlerApp extends AbstractContestHandler { createContest(contest) { } deleteContest(uid: string) { } updateContest(contest) { } getContest(uid: string) { return null; } getContests() { window.Electron.ipcRenderer.send(FETCH_DATA_FROM_STORAGE, "contests") return []; } }
报错信息
ERROR in ./node_modules/electron/index.js 1:11-24 Module not found: Error: Can't resolve 'fs' in '/home/username/Desktop/NextPetanque/app/node_modules/electron' ERROR in ./node_modules/electron/index.js 2:13-28 Module not found: Error: Can't resolve 'path' in '/home/username/Desktop/NextPetanque/app/node_modules/electron' BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default. This is no longer the case. Verify if you need this module and configure a polyfill for it. If you want to include a polyfill, you need to: - add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }' - install 'path-browserify' If you don't want to include a polyfill, you can use an empty module like this: resolve.fallback: { "path": false }
解决方案
1. 配置Electron预加载脚本(官方推荐安全方案)
在public目录下新建preload.js,用contextBridge将ipcRenderer的方法暴露给渲染进程:
const { ipcRenderer, contextBridge } = require('electron'); // 暴露指定API给前端,避免直接暴露ipcRenderer contextBridge.exposeInMainWorld('ElectronAPI', { fetchDataFromStorage: (arg) => ipcRenderer.send('FETCH_DATA_FROM_STORAGE', arg) });
2. 修改Electron主进程配置
更新electron.js中的BrowserWindow创建代码,添加预加载脚本路径和安全配置:
const createWindow = () => { const win = new BrowserWindow({ width: 1080, height: 720, webPreferences: { // 指定预加载脚本路径 preload: path.join(__dirname, 'preload.js'), // 开启上下文隔离,防止渲染进程篡改主进程API contextIsolation: true, // 禁用Node集成,提升安全性 nodeIntegration: false } }) win.loadURL( isDev ? 'http://localhost:3000' : "file://" + path.join(__dirname, "../build/index.html") ) }
3. 修正React组件代码
移除直接引入electron的代码,改用预加载脚本暴露的全局API:
// 删掉这一行:const { ipcRenderer } = require('electron'); export class ContestHandlerApp extends AbstractContestHandler { // ...其他方法保持不变 getContests() { // 使用暴露的ElectronAPI window.ElectronAPI.fetchDataFromStorage("contests") return []; } }
4. 可选:禁止Webpack解析electron模块
在package.json中添加以下配置,防止Webpack尝试打包electron模块:
"browser": { "electron": false }
原理说明
报错原因是在React代码中直接require('electron'),Webpack会尝试解析electron模块的依赖(如Node.js核心模块fs、path),但这些模块在浏览器/渲染进程环境中不存在。使用预加载脚本+contextBridge是Electron官方推荐的方式,既能安全地向渲染进程暴露主进程API,又能避免Webpack打包时解析Node.js核心模块导致的错误。
内容的提问来源于stack exchange,提问作者Antox
相关产品推荐
相关产品推荐

