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

使用Next.js+Electron模板导入ipcRenderer时遇fs模块找不到错误

Next.js + Electron 中导入 ipcRenderer 出现 "Can't resolve 'fs'" 错误的解决方法

问题场景

使用 Next.js 结合 Electron 模板开发时,在 pages/index.tsx 中直接导入 ipcRenderer 后,执行 npm run dev 出现以下错误:

../node_modules/electron/index.js:1:0
Module not found: Can't resolve 'fs'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./pages/home/index.tsx

用到的关键代码:

...
import { ipcRenderer } from 'electron';


function Home() {

  useEffect(() => {
    ipcRenderer.on("getMember-reply", (event, data) => {
      console.log(data); 
    });
    return () => {                     
      ipcRenderer.removeAllListeners("getMember-reply");
    };
  }, [])

  const handleClick = () => {
    if (typeof window === 'undefined') {
      ipcRenderer.send("getMember", {});     
    }else {
      return
    }
  }

  return (
   <>
    <h1>home</h1>
    <Button onClick={handleClick}> Click Me</Button>
   </>

  );
};

export default Home;

原因分析

Next.js 服务端渲染阶段会尝试解析 Electron 模块,但 Electron 依赖的 fs 等模块仅存在于 Node.js 环境(Electron 主进程),浏览器/客户端环境没有对应实现,因此触发模块找不到的错误。

解决步骤

1. 避免服务端直接解析 Electron 模块

替换直接导入的方式,仅在客户端环境访问 ipcRenderer。可以通过 window.require 动态获取,或者利用 Electron 暴露到全局的对象:

修改后的页面代码:

import { useEffect } from 'react';
import Button from 'your-button-component-path'; // 替换为实际组件路径

function Home() {
  useEffect(() => {
    // 仅在客户端环境初始化监听
    const ipcRenderer = window.require?.('electron').ipcRenderer;
    if (!ipcRenderer) return;

    const handleReply = (event: any, data: any) => {
      console.log(data);
    };

    ipcRenderer.on("getMember-reply", handleReply);
    return () => {
      // 推荐用removeListener精准移除,避免误删其他监听
      ipcRenderer.removeListener("getMember-reply", handleReply);
    };
  }, [])

  const handleClick = () => {
    const ipcRenderer = window.require?.('electron').ipcRenderer;
    if (ipcRenderer) {
      ipcRenderer.send("getMember", {});
    }
  }

  return (
   <>
    <h1>home</h1>
    <Button onClick={handleClick}> Click Me</Button>
   </>
  );
};

export default Home;

2. 配置 Next.js 忽略 Electron 服务端解析

在项目根目录创建或修改 next.config.js,添加 webpack 配置,告诉 Next.js 在服务端编译时跳过 Electron 模块的解析:

/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: (config, { isServer }) => {
    if (!isServer) {
      // 客户端编译时将electron模块置为空,避免解析错误
      config.resolve.fallback = {
        ...config.resolve.fallback,
        electron: false
      };
    }
    return config;
  },
};

module.exports = nextConfig;

3. 上下文隔离模式下的正确做法(可选)

如果 Electron 主进程开启了 contextIsolation: true(默认开启),需要通过预加载脚本安全暴露 ipcRenderer 给渲染进程:

主进程配置(示例:main.js)

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

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'), // 指定预加载脚本路径
      contextIsolation: true,
      nodeIntegration: false
    }
  });

  mainWindow.loadURL('http://localhost:3000'); // 加载Next.js开发服务
}

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, ipcRenderer } = require('electron');

// 安全暴露API到渲染进程全局
contextBridge.exposeInMainWorld('electronAPI', {
  onGetMemberReply: (callback) => ipcRenderer.on('getMember-reply', callback),
  sendGetMember: (data) => ipcRenderer.send('getMember', data),
  removeGetMemberReplyListener: (callback) => ipcRenderer.removeListener('getMember-reply', callback)
});

页面中使用暴露的API

import { useEffect } from 'react';
import Button from 'your-button-component-path';

function Home() {
  useEffect(() => {
    const handleReply = (event: any, data: any) => {
      console.log(data);
    };

    window.electronAPI?.onGetMemberReply(handleReply);
    return () => {
      window.electronAPI?.removeGetMemberReplyListener(handleReply);
    };
  }, [])

  const handleClick = () => {
    window.electronAPI?.sendGetMember({});
  }

  return (
   <>
    <h1>home</h1>
    <Button onClick={handleClick}> Click Me</Button>
   </>
  );
};

export default Home;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 05:25:28