javascript - 在带有 Webpack 的 Electron 渲染器中使用 Node.js 插件

标签 javascript node.js webpack electron node-serialport

我有以下渲染器:

import SerialPort from "serialport";

new SerialPort("/dev/tty-usbserial1", { baudRate: 57600 });

它由 Webpack 构建,具有以下配置(为简洁起见进行了 trim ):

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)

我该如何解决这个问题?

最佳答案

问题

第一个问题是 node-bindingsnode-serialport 依赖它来解析其 Node.js 插件的路径,在 Electron 中根本不起作用.有一个 open issue为此,我不认为相关的 PR 甚至是一个完整的修复,因为我已经做了一些调试,而且 fileName 似乎在整个过程中仍然是 undefined 获取文件名

第二个问题:即使它以某种方式在某处找到了 serialport.node,在打包应用程序以进行分发后它也不会工作,因为插件本身不在 dist 中 目录,Webpack 不能将它与主 JS 文件捆绑在一起。

可以尝试用 node-loader 来解决这个问题,给定一个正确工作的 node-bindings,但这也无济于事,因为 node-bindings 使用精心设计的启发式算法,Webpack 在尝试时根本无法从中推断了解其 require 可能需要哪些文件。 Webpack 可以做的唯一安全的事情是包含整个项目,“以防万一”,显然,这是绝对不行的,所以 node-loader 只是不复制任何东西。

因此,我们需要替换node-bindings并手动复制serialport.node

解决方案

首先,我们必须获取插件并将其放入dist。这需要在 main 的 Webpack 构建中完成,因为渲染器作为网页提供,可能来自内存文件系统(因此 *.node 文件可能不会发送到磁盘,而 Electron永远不会看到它)。方法如下:

import CopyWebpackPlugin from "copy-webpack-plugin";

const config = {
  // ...
  plugins: [
    new CopyWebpackPlugin([
      "node_modules/serialport/build/Release/serialport.node",
    ]),
  ],
  // ...
};

不幸的是,它是硬编码的,但如果有什么变化很容易修复。

其次,我们必须将 node-bindings 替换为我们自己的 shim,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-packagernode_modules 的整个生产部分复制到 dist 以获取你,但那是一大堆兆字节,基本上什么都没有。

关于javascript - 在带有 Webpack 的 Electron 渲染器中使用 Node.js 插件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50547649/

相关文章:

node.js - 使用 Express 从 NodeJS 服务器下载 .docx 文件

node.js - 在应用程序生命周期内保持服务总线连接处于事件状态

webpack - 如何告诉 Webpack 不要打包配置文件

javascript - 如何在 ui-router 中使用 Angular 组件?

javascript - 是否可以在 Java 或 JavaScript 中发出 HTTP (POST) 请求而不等待响应

node.js - 如何解决 amqplib Channel#consume 奇怪的签名?

javascript - 模块构建失败 : Error: Couldn't find preset "transform-class-properties" relative to directory

angular - Angular 2 中的主题 'Material Design Components for Web'

javascript - 优先加载图像的幻灯片放映

javascript - javascript 正则表达式中的可选部分(带捕获组)