在Webpack的Electron渲染器中使用Node.js插件 [英] Using Node.js addons in Electron's renderer with Webpack

查看:163
本文介绍了在Webpack的Electron渲染器中使用Node.js插件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下渲染器:

import SerialPort from "serialport";

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

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

It's built by Webpack, with the following config (trimmed for brevity):

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 一起提供服务,并由主进程作为网页加载(开发过程中热模块更换需要此页面).

It's served by a development server, along with an index.html, and is loaded by the main process as a web page (this is needed for hot module replacement during development).

主要流程是由Webpack构建的,并也发送到 dist .Webpack插件还生成以下 dist/package.json :

The main process is built by Webpack and emitted to dist too. A Webpack plugin also generates the following dist/package.json:

{
  "name": "my-app",
  "main": "main.js"
}

当我运行 electron dist 时,渲染器进程崩溃,并出现以下错误:

When I run electron dist, the renderer process crashes with the following error:

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-serialport 用来解析其Node.js插件路径的 node-bindings 根本在Electron中不起作用.为此存在一个公开问题,我不认为相关的PR是甚至是一个完整的修复程序,因为我已经进行了一些调试,并且在整个 getFileName 中, fileName 似乎仍然是 undefined .

Problem

The first problem is that node-bindings, which node-serialport relies on to resolve the path to its Node.js addon, simply doesn't work in Electron. There's an open issue for this, and I don't think the associated PR is even a complete fix, since I've done some debugging, and it appears that fileName remains undefined throughout the whole getFileName.

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

The second problem: even if it somehow found a serialport.node somewhere, it wouldn't work after packaging the application for distribution, since the addon itself isn't in the dist directory, and Webpack can't just bundle it together with the main JS file.

可以尝试使用 node-loader ,并给出了正确的 node-bindings ,但也无济于事,因为 node-bindings 使用了精心设计的启发式方法,Webpack根本无法从中推断出试图了解其 require 可能需要哪些文件.Webpack唯一可以做的安全的事情就是包括整个项目,以防万一,这显然是不行的,所以 node-loader 只是不复制任何内容.

One could attempt to solve this with node-loader, given a correctly working node-bindings, but that wouldn't help either, since node-bindings uses elaborate heuristics, which Webpack simply can't extrapolate from, when trying to understand what files could be required by its require. The only safe thing Webpack could do is include the whole project, "just in case", and that's a certain no-go, obviously, so node-loader just doesn't copy anything.

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

So, we need to replace node-bindings and copy serialport.node manually.

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

First, we must grab the addon and put it in dist. This needs to be done in main's Webpack build, since the renderer is served as web page, potentially from an in-memory file system (so the *.node file may not be emitted to disk, and Electron will never see it). Here's how:

import CopyWebpackPlugin from "copy-webpack-plugin";

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

不幸的是,经过硬编码,但是如果发生更改,很容易修复.

Hardcoded, unfortunately, but easy to fix if something changes.

第二,我们必须用自己的垫片 src/bindings.js 替换 node-bindings :

Second, we must substitute node-bindings with our own 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内部的某个目录中.

__non_webpack_require__ is self-explanatory (yes, plain require won't work, without some trickery, as it's handled by Webpack), and the require("electron").remote.app.getAppPath() is necessary because __dirname doesn't actually resolve to what one would expect - an absolute path to dist - but rather to some directory buried deep inside Electron.

这是在渲染器的Webpack配置中完成替换的方法:

And here's how the replacement is done, in renderer's Webpack config:

import { NormalModuleReplacementPlugin } from "webpack";

const config = {
  // ...
  plugins: [
    new NormalModuleReplacementPlugin(
      /^bindings$/,
      `${__dirname}/src/bindings`
    ),
  ],
  // ...
};

就是这样!完成上述步骤后, index.html + renderer.js 将由某些服务器(或您采用的任何方法)以及 dist 看起来像这样:

And that's it! Once the above is done, and index.html + renderer.js are being served by some server (or whatever your approach is), and the dist looks something like this:

dist/
  main.js
  package.json
  serialport.node

电子距离应该正常工作".

通过添加 node-serialport 作为对所生成的 dist/package.json 的依赖项,并可能只是 npm i 安装它,可能会避免在其中,并在Webpack中将 serialport 标记为外部,但这感觉甚至更脏(软件包版本不匹配等).

Could potentially get away with adding node-serialport as a dependency to the generated dist/package.json and just npm installing it in there, and marking serialport as an external in Webpack, but that feels even dirtier (package version mismatches, etc.).

另一种方法是将所有内容声明为外部对象,并使用 electron-packager node_modules 的整个生产部分复制到 dist 您,但实际上根本就是一整兆.

Another way is to just declare everything as externals, and have electron-packager just copy the whole production part of node_modules to dist for you, but that's a whole lot of megabytes for basically nothing.

这篇关于在Webpack的Electron渲染器中使用Node.js插件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆