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

React+Electron读取本地文件时fs模块找不到的问题求助

解决Electron+React+Webpack环境下fs/path模块无法解析的问题

问题核心

React渲染进程默认运行在浏览器环境,Webpack会按浏览器标准处理模块,而fs/path是Node.js核心模块,直接在渲染进程中引入会导致解析错误。同时Electron新版本默认关闭了nodeIntegration,进一步限制了渲染进程对Node模块的直接访问。

解决方案

1. 配置Electron主进程(推荐安全方案:Preload脚本)

避免直接开启nodeIntegration(存在安全风险),改用Electron官方推荐的Preload脚本在隔离环境中暴露必要的文件操作API:

修改electron.js:

const { app, BrowserWindow } = require('electron');
const path = require('path');

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      // 开启上下文隔离(默认开启,安全)
      contextIsolation: true,
      // 关闭直接Node集成
      nodeIntegration: false,
      // 指定Preload脚本路径
      preload: path.join(__dirname, 'preload.js')
    }
  });

  // 加载React开发服务或打包后的html
  mainWindow.loadURL('http://localhost:3000'); // 开发环境
  // mainWindow.loadFile(path.join(__dirname, '../build/index.html')); // 生产环境
}

app.whenReady().then(() => {
  createWindow();
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

创建preload.js:

const { contextBridge } = require('electron');
const fs = require('fs');
const path = require('path');
const { app } = require('electron').remote;

// 向渲染进程暴露安全的API
contextBridge.exposeInMainWorld('electronFileAPI', {
  // 读取JSON文件
  readJSON: (fileName) => {
    const filePath = path.join(app.getPath('userData'), fileName); // 推荐用Electron的userData目录存储数据
    if (fs.existsSync(filePath)) {
      const data = fs.readFileSync(filePath, 'utf-8');
      return JSON.parse(data);
    }
    return {};
  },
  // 写入JSON文件
  writeJSON: (fileName, data) => {
    const filePath = path.join(app.getPath('userData'), fileName);
    fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
  }
});

2. 调整Webpack配置

修改webpack.config.js,指定target为Electron渲染进程,并正确处理Node模块:

const path = require('path');

module.exports = {
  entry: './src/index.js', // 你的React入口文件
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js'
  },
  // 指定目标环境为Electron渲染进程
  target: 'electron-renderer',
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx'],
    // 处理Node模块的 fallback(Webpack 5+ 需配置)
    fallback: {
      "path": require.resolve("path-browserify"),
      "fs": false // 通过Preload脚本获取,无需Webpack打包
    }
  },
  // 告知Webpack不打包Node核心模块,直接使用Electron提供的版本
  externals: {
    'fs': 'commonjs fs',
    'path': 'commonjs path'
  },
  devServer: {
    static: {
      directory: path.join(__dirname, 'public'),
    },
    port: 3000,
    hot: true
  }
};

3. 在React组件中调用API

在App.js中通过暴露的全局API进行文件操作:

import { useState, useEffect } from 'react';

function App() {
  const [cashData, setCashData] = useState({});

  // 加载数据
  useEffect(() => {
    const data = window.electronFileAPI.readJSON('cashHistory.json');
    setCashData(data);
  }, []);

  // 保存数据
  const saveCashData = (newRecord) => {
    const updatedData = {
      ...cashData,
      records: [...(cashData.records || []), newRecord]
    };
    window.electronFileAPI.writeJSON('cashHistory.json', updatedData);
    setCashData(updatedData);
  };

  return (
    <div className="App">
      <h1>收银台资金统计</h1>
      <button onClick={() => saveCashData({
        time: new Date().toLocaleString(),
        amount: Math.floor(Math.random() * 1000)
      })}>添加销售记录</button>
      <div>
        <h3>历史记录</h3>
        <ul>
          {cashData.records?.map((item, index) => (
            <li key={index}>{item.time} - ¥{item.amount}</li>
          ))}
        </ul>
      </div>
    </div>
  );
}

export default App;

4. 检查package.json配置

确保脚本命令正确,依赖完整:

{
  "name": "cash-register-app",
  "version": "1.0.0",
  "main": "electron.js",
  "scripts": {
    "start": "electron .",
    "dev": "webpack serve --mode development & electron .",
    "build": "webpack --mode production"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@babel/core": "^7.22.0",
    "@babel/preset-react": "^7.22.0",
    "babel-loader": "^9.1.3",
    "electron": "^25.0.0",
    "path-browserify": "^1.0.1",
    "webpack": "^5.88.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  }
}

关键注意事项

  • 不要在渲染进程中直接require('fs'),必须通过Preload脚本或IPC通信让主进程处理文件操作(符合Electron安全规范)
  • 存储数据时推荐使用Electron的app.getPath('userData')目录,避免权限问题(用户主目录下的应用专属文件夹)
  • Webpack的target必须设置为electron-renderer,否则会把Node模块编译为浏览器兼容版本,导致错误
  • 如果使用Webpack 5,需要配置fallback处理一些Node模块的浏览器兼容版本(比如path-browserify)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:23:16