Electron渲染进程结合Webpack使用Node.js插件报错的解决方法
我有如下渲染进程代码:
import SerialPort from "serialport"; new SerialPort("/dev/tty-usbserial1", { baudRate: 57600 });
该代码由Webpack构建,相关配置(简化后)如下:
const config = { entry: { renderer: ["./src/renderer"] }, output: { path: `${__dirname}/dist`, filename: "[name].js", }, target: "electron-renderer", node: false, // 禁用__dirname模拟等功能 };
代码由开发服务器提供服务,搭配index.html,并由主进程作为网页加载(这是开发时热模块替换所需)。
主进程同样由Webpack构建并输出到dist目录,Webpack插件还生成了如下dist/package.json:
{ "name": "my-app", "main": "main.js" }
当我运行electron dist时,渲染进程崩溃并抛出如下错误:
Uncaught TypeError: Path must be a string. Received undefined at assertPath (path.js:28) at dirname (path.js:1364) at Function.getRoot (bindings.js?dfc1:151) at bindings (bindings.js?dfc1:60) at eval (linux.js?d488:2) at Object../node_modules/serialport/lib/bindings/linux.js (renderer.js:12686) at __webpack_require__ (renderer.js:712) at fn (renderer.js:95) at eval (auto-detect.js?3cc7:16) at Object../node_modules/serialport/lib/bindings/auto-detect.js (renderer.js:12638)
请问该如何解决这个问题?
这个问题本质是serialport这类原生Node.js模块在Electron渲染进程被Webpack打包时的路径解析冲突——你设置node: false完全禁用了Node.js全局变量模拟,导致serialport依赖的bindings模块无法正确获取文件路径。下面给你几个实用的解决思路:
1. 让Webpack跳过serialport的打包
原生模块包含二进制文件,本来就不适合被Webpack打包处理。你可以在Webpack配置里添加externals,告诉Webpack直接在运行时通过require加载serialport:
const config = { // ... 其他原有配置 externals: { serialport: "require('serialport')" } };
这样Webpack会把渲染进程里的import SerialPort from 'serialport'替换为运行时的require('serialport'),让Electron直接从node_modules加载原生模块,避免路径解析错误。
2. 按需启用Node.js全局变量模拟
你不需要完全禁用所有Node.js全局变量,只需要启用bindings模块需要的__dirname和__filename即可:
const config = { // ... 其他原有配置 node: { __dirname: true, __filename: true } };
这种方式既能保留你禁用其他Node.js全局变量的需求,又能让serialport的依赖模块正确解析路径。
3. 主进程处理串口逻辑,通过IPC和渲染进程通信
这是更符合Electron安全规范的做法——渲染进程如果开启Node.js集成存在安全风险,而原生模块操作放在主进程更稳妥。你可以通过Electron的IPC机制实现渲染进程和主进程的通信:
主进程代码示例
const { ipcMain } = require('electron'); const SerialPort = require('serialport'); // 初始化串口 const port = new SerialPort("/dev/tty-usbserial1", { baudRate: 57600 }); // 接收渲染进程的发送数据请求 ipcMain.handle('serial-send-data', async (event, data) => { port.write(data); }); // 向渲染进程传递串口接收的数据 ipcMain.on('serial-listen-data', (event) => { port.on('data', (data) => { event.reply('serial-received-data', data.toString()); }); });
渲染进程代码示例
const { ipcRenderer } = require('electron'); // 发送数据到串口 ipcRenderer.invoke('serial-send-data', 'Hello Serial Port!'); // 监听串口返回的数据 ipcRenderer.on('serial-received-data', (event, data) => { console.log('Received from serial:', data); });
额外注意事项
- 如果你的
serialport安装后无法正常运行,记得用electron-rebuild重新编译适配Electron版本的原生模块:npx electron-rebuild - 开发模式下使用热加载时,主进程的修改需要重启Electron才能生效,可以搭配
electronmon这类工具实现主进程自动重启。
内容的提问来源于stack exchange,提问作者Alec Mev

