我有以下渲染器:
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, // Disables __dirname mocking and such
};
它由开发服务器和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)
我该如何解决这个问题?
答案 0 :(得分:2)
第一个问题是node-bindings
node-serialport
依赖于解析其Node.js插件的路径,根本不在Electron中工作。我有一个open issue,我不认为相关的PR甚至是一个完整的解决方案,因为我已经完成了一些调试,看起来fileName
在整个undefined
中保持getFileName
。
第二个问题:即使它以某种方式在某个地方找到了serialport.node
,但在打包应用程序以进行分发之后它也无法工作,因为插件本身并不在dist
中目录,Webpack不能将它与主JS文件捆绑在一起。
考虑到正确工作node-bindings
,有人可以尝试使用node-loader
来解决此问题,但这也不会有任何帮助,因为node-bindings
使用了精心设计的启发式算法,Webpack可以简单地使用当尝试理解require
可能需要哪些文件时,可以推断出来。 Webpack可以做的唯一安全的事情就是包括整个项目,"以及#34;并且这显然是禁止的,显然,node-loader
只是不复制任何东西。
因此,我们需要手动替换node-bindings
并复制serialport.node
。
首先,我们必须抓住插件并将其放入dist
。这需要在主要的Webpack构建中完成,因为渲染器可能作为网页提供,可能来自内存中的文件系统(因此*.node
文件可能不会发送到磁盘,而Electron将永远不会看到它。以下是:
import CopyWebpackPlugin from "copy-webpack-plugin";
const config = {
// ...
plugins: [
new CopyWebpackPlugin([
"node_modules/serialport/build/Release/serialport.node",
]),
],
// ...
};
不幸的是,硬编码,但如果发生变化,很容易修复。
其次,我们必须将node-bindings
替换为我们自己的垫片src/bindings.js
:
module.exports = x =>
__non_webpack_require__(
`${require("electron").remote.app.getAppPath()}/${x}`
);
__non_webpack_require__
是不言自明的(是的,普通require
无法工作,没有一些技巧,因为它由Webpack处理)和require("electron").remote.app.getAppPath()
是必要的,因为__dirname
实际上并没有达到预期的目标 - dist
的绝对路径 - 而是埋藏在Electron深处的某个目录。
以及如何在渲染器的Webpack配置中完成替换:
import { NormalModuleReplacementPlugin } from "webpack";
const config = {
// ...
plugins: [
new NormalModuleReplacementPlugin(
/^bindings$/,
`${__dirname}/src/bindings`
),
],
// ...
};
那就是它!完成上述操作后,某个服务器(或任何方法)提供index.html
+ renderer.js
,dist
看起来像这样:
dist/
main.js
package.json
serialport.node
electron dist
应该"只是工作"。
可能会将node-serialport
作为依赖项添加到生成的dist/package.json
,只是npm i
将其添加到其中,并将serialport
标记为Webpack中的外部,但这感觉更脏(包装版本不匹配等)。
另一种方法是将所有内容声明为外部,并electron-packager
只是将node_modules
的整个制作部分复制到dist
给你,但那很多都是几乎没有兆字节。
答案 1 :(得分:0)
我要感谢@Olegs Jeremejevs实际上是整个互联网上唯一真正有效并且具有真正知识的答案。我花了数天的时间来解决webpack和电子本机模块的问题,直到尝试了Oleg的答案,它才能正常工作。
仅需少量补充,如果在主进程中使用本机代码而不是在渲染器中使用本机代码(最常见的情况是应用程序的安全性更高),则需要对垫片进行如下调整:
module.exports = x =>
__non_webpack_require__(
`${require('electron').app.getAppPath()}/${x}`
);
答案 2 :(得分:0)
在撰写本文时,我认为@Alec Mev
的答案是惊人的。
就我而言,所有互联网建议的黑客攻击(例如复制,加载程序等)均未成功,而且还不清楚。
我想通过电子渲染器调用我自己的本机节点插件,但是正如Alec Webpack所提到的,它不能按要求处理.node文件。所以就我而言:
文件夹结构
Electron project dir
│ electron.main.js
│ package.json
│
├───Addon
│ │ addon.js
│ │ package.json
│ │
│ └───dist
│ addon.node
│
└───render-site
│ index.html
│ js.js
│ webpack.config.js
│
└───build
render-site
是您的webpack网站,dist
是您的附加二进制文件位置。非常重要的是将webpack目标设置为'electron-renderer'
,它使您可以更轻松地使用electron
和其他节点模块(例如fs
)的需求。
在您的网站中,像建议的docs一样调用插件
const addon = require('electron').remote.require('./addon/dist/addon.node');
就是这样。您可以使用插件运行webpack并为网站提供服务。
生产非常简单,您只需确保将附件复制到电子版中(我正在使用电子版复制系统),并具有指向electronic.main.js的相对路径。
希望有帮助。
答案 3 :(得分:0)
尝试在electron-renderer
中使用本机模块时遇到类似的错误。
尽管在我的情况下错误消息是这样的:
node-loader:找不到指定的模块
我相信这都与节点加载器有关。对我来说,native-addon-loader是非常有效的装载程序。
另外,请确保您的Webpack目标是node
,electron-main
或electron-renderer
,否则它将不起作用(客户端上不能使用本机模块)。