在Webpack中使用Electron的渲染器中的Node.js插件

时间:2018-05-26 22:57:49

标签: javascript node.js webpack electron node-serialport

我有以下渲染器:

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)

我该如何解决这个问题?

4 个答案:

答案 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.jsdist看起来像这样:

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目标是nodeelectron-mainelectron-renderer,否则它将不起作用(客户端上不能使用本机模块)。