Electron Module not found Error: Can‘t resolve ‘fs‘ 或 fs.existsSync&&ipcRendere.send is not function


title: 【Electron Module】 not found Error Can’t resolve ‘fs’
author:十六进制
date: 2024-4-20 17:11:00
sidebar: auto
tags:

  • 学习
  • Electron
  • Vue.js
  • 前端
  • 记录问题
    categories:
  • bug

一、发现问题

最近开始上手用electron将vue项目打包成一个可执行程序,其中有个需求,就是electron自带的frame工具栏,以及最大化,最小化,关闭按钮等样式太丑了,于是我就想在自己的vuecli组件中引入electron的ipcRenderer模块,想通过ipcRenderer.send方法将关闭窗口的消息发送给主进程,最开始的尝试代码如下:

// 在 Electron 主进程中 (main.js)
const { app, ipcMain } = require('electron');

ipcMain.on('close-app', () => {
  // 在此执行关闭窗口的操作
  app.quit();
});
// 在 Vue 组件中
methods: {
  handleCloseButtonClick() {
    // 在 Vue 组件中引入 Electron 模块
    const { ipcRenderer } = require('electron');
    // 发送消息给主进程,请求退出程序
    window.ipcRenderer.send('quit-app');    //方式1
    window.electron.ipcRenderer.send('quit-app');   //方式2
  }
}

结果:在运行npm run build尝试打包我的vue项目时就报错了,提示没有安装electron模块,于是我又在我的vue工程下运行如下代码安装该模块:

npm install electron --save-dev

在运行npm run build 时,又报错了,提示:Module not found: Error: Can’t resolve ‘fs’ && Module not found: Error: Can’t resolve ‘path’

我又尝试了在vue.config.js中对webpack进行配置:

configureWebpack: {
    externals: {
      "fs": 'require("fs")',
      "path": 'require("path")',
      electron: 'require("electron")'         // 避免打包 Electron 模块
    },
    resolve: {
      fallback: {
        "fs": false,           //Webpack将不再尝试解析Electron模块中的fs模块  无效,无法解决根本问题
        "path": require.resolve("path-browserify"),   //Webpack会使用path-browserify来替代Node.js中的path模块
      }
    },
  },

但是,无论是externals还是resolve,fallback,我都试过了,都无效,报错都是以下信息:

[electron]fs.existsSync is not a function

或者

ipcRenderer.send is not a function

直到我发现了这篇文章,我以为自己就快要解决问题了,结果最后还是失败了,我在想应该是我和博主使用的electron版本不一致,我使用的是目前对我这个时间来说最新的版本"electron": "^30.0.1"。所以最后我还是跑到了官网去查最新的api,果然功夫不负有心人,被我找到了最终的解决方案,全网都没找到的方法,结果就在官方文档上,所以下次再遇到类似问题,就请直接去看官方文档!

下面提供官方文档的链接:官方文档
请直接看》》模式 1:渲染器进程到主进程(单向),因为官方提供了一个完整的代码演示,可以直接在Electron Fiddle中打开。

二、官方文档的解决方案

1、首先在主进程中

//main.js
const { app, BrowserWindow, ipcMain } = require('electron/main')

ipcMain.on('set-title', (event, title) => {
    const webContents = event.sender
    const win = BrowserWindow.fromWebContents(webContents)
    win.setTitle(title)
  })

还是选择了用ipcMain.on方法来监听渲染进程发给主进程的消息事件。

2、然后在渲染进程中

//renderer.js
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
  const title = titleInput.value
  window.electronAPI.setTitle(title)
})

注意:使用的是window.electronAPI.setTitle(title),而不是window.ipcRenderer.send('set-title', title)

3、重点来了,在预加载脚本preload.js中,绑定一个electronAPI对象,用来与主进程通信

//preload.js
const { contextBridge, ipcRenderer } = require('electron/renderer')

contextBridge.exposeInMainWorld('electronAPI', {
  setTitle: (title) => ipcRenderer.send('set-title', title)
})

我首先在Electron Fiddle中尝试了官方提供的进程间通信示例,主要是实现在渲染进程中点击一个按钮,在主进程中设置窗口的标题。
于是我在官方的示例上改进了我的代码。

三、我的最终解决方案

1、在主进程中

const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 1920,
    height: 1080,
    fullscreen: true, // 自动全屏
    frame: false, // 隐藏默认工具栏
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),      // 添加预加载脚本
      nodeIntegration: true,
      // contextIsolation: false,
    }
  })
}
// 监听自己的关闭按钮事件,然后退出应用程序
ipcMain.on('quit-app', () => {
  app.quit();
});

2、在我的渲染进程中,也就是我的vue组件中

methods: {
    exitClick() {
        console.log(window.electronAPI);
        window.electronAPI.quitApp();       //在这里调用主进程的quit-app事件
    },
}

3、在预加载脚本中(最主要的一步,作用就是把主进程中的ipcRenderer.send事件暴露给渲染进程)

const { contextBridge, ipcRenderer } = require('electron/renderer')

contextBridge.exposeInMainWorld('electronAPI', {
    quitApp: () => ipcRenderer.send('quit-app')     //暴露给渲染进程的quit-app事件
})

4、下面提供我的完整代码

// main.js

// Modules to control application life and create native browser window
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('node:path')
const url = require('url');

const createWindow = () => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 1920,
    height: 1080,
    fullscreen: true, // 自动全屏
    frame: false, // 隐藏默认工具栏
    icon: './myapp/dist/img/logo.8d90e46a.png',
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: true,
      // contextIsolation: false,
    }
  })

  // 加载 dist 文件夹中的 index.html
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'myapp', 'dist','index.html'),
    protocol: 'file:',
    slashes: true
  }));

  // 打开开发工具
  // mainWindow.webContents.openDevTools()
}

// 这段程序将会在 Electron 结束初始化
// 和创建浏览器窗口的时候调用
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    // 在 macOS 系统内, 如果没有已开启的应用窗口
    // 点击托盘图标时通常会重新创建一个新窗口
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此, 通常
// 对应用程序和它们的菜单栏来说应该时刻保持激活状态, 
// 直到用户使用 Cmd + Q 明确退出
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

// 在当前文件中你可以引入所有的主进程代码
// 也可以拆分成几个文件,然后用 require 导入。

// 监听自己的关闭按钮事件,然后退出应用程序
ipcMain.on('quit-app', () => {
  app.quit();
});
// preload.js

const { contextBridge, ipcRenderer } = require('electron/renderer')

contextBridge.exposeInMainWorld('electronAPI', {
    // setTitle: (title) => ipcRenderer.send('set-title', title),           //官网示例
    quitApp: () => ipcRenderer.send('quit-app')
})
//vue文件

methods: {
    exitClick() {
        this.$confirm('确定要退出吗?' , {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
        }).then(() => {
            console.log(window.electronAPI);
            window.electronAPI.quitApp();       //在这里调用主进程的quit-app事件
        }).catch(() => {
            console.log("取消退出app");
        });
},
}
  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值