問題描述
我有以下渲染器:
import SerialPort from "serialport";
new SerialPort("/dev/tty-usbserial1", { baudRate: 57600 });
它由 Webpack 構(gòu)建,具有以下配置(為簡潔起見):
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
};
它由開發(fā)服務(wù)器提供服務(wù),與 index.html
一起,并由主進(jìn)程作為網(wǎng)頁加載(這是開發(fā)過程中熱模塊更換所需要的).
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).
主進(jìn)程由 Webpack 構(gòu)建并發(fā)送到 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"
}
當(dāng)我運(yùn)行 electron dist
時,渲染器進(jìn)程崩潰并出現(xiàn)以下錯誤:
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)
我該如何解決這個問題?
How do I fix this?
推薦答案
問題
第一個問題是 node-bindings
,node-serialport
依賴于解析其 Node.js 插件的路徑,在 Electron 中根本不起作用.有一個 未決問題 ,我認(rèn)為相關(guān)的 PR 不是甚至是一個完整的修復(fù),因?yàn)槲乙呀?jīng)進(jìn)行了一些調(diào)試,并且似乎 fileName
在整個 getFileName
中仍然是 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
,在打包應(yīng)用程序以進(jìn)行分發(fā)后它也無法工作,因?yàn)椴寮旧聿辉?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
,但這也無濟(jì)于事,因?yàn)?node-bindings
使用精細(xì)的啟發(fā)式方法,Webpack 根本無法從中推斷,當(dāng)試圖了解其 require
可能需要哪些文件.Webpack 可以做的唯一安全的事情是包含整個項(xiàng)目,以防萬一",顯然這是絕對不行的,所以 node-loader
只是不復(fù)制任何東西.
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
并復(fù)制serialport.node
.
So, we need to replace node-bindings
and copy serialport.node
manually.
首先,我們必須抓取插件并將其放入dist
.這需要在 main 的 Webpack 構(gòu)建中完成,因?yàn)殇秩酒髯鳛榫W(wǎng)頁提供,可能來自內(nèi)存中的文件系統(tǒng)(因此 *.node
文件可能不會發(fā)送到磁盤,而 Electron永遠(yuǎn)不會看到它).方法如下:
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",
]),
],
// ...
};
不幸的是,硬編碼,但如果發(fā)生變化,很容易修復(fù).
Hardcoded, unfortunately, but easy to fix if something changes.
其次,我們必須用我們自己的 shim 替換 node-bindings
,src/bindings.js
:
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
不起作用,沒有一些技巧,因?yàn)樗?Webpack 處理),并且 require("electron").remote.app.getAppPath()
是必要的,因?yàn)?__dirname
實(shí)際上并沒有解決人們所期望的 - 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
正在由某個服務(wù)器(或任何您的方法)提供服務(wù),并且 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
electron dist
應(yīng)該正常工作".
將 node-serialport
作為對生成的 dist/package.json
的依賴項(xiàng)添加并且只需 npm i
nstalling 可能會成功在那里,并將 serialport
標(biāo)記為 Webpack 中的外部,但這感覺更臟(包版本不匹配等).
Could potentially get away with adding node-serialport
as a dependency to the generated dist/package.json
and just npm i
nstalling it in there, and marking serialport
as an external in Webpack, but that feels even dirtier (package version mismatches, etc.).
另一種方法是將所有內(nèi)容聲明為外部,并讓 electron-packager
只需將 node_modules
的整個生產(chǎn)部分復(fù)制到 dist
即可你,但那是一大堆兆字節(jié),基本上什么都沒有.
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.
這篇關(guān)于在帶有 Webpack 的 Electron 渲染器中使用 Node.js 插件的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!