一、按照惯例先说点废话

  Electron: Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。 Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的。

  Node.js: 简单的说 Node.js 就是运行在服务端的 JavaScript,是一个基于Chrome JavaScript 运行时建立的一个平台,Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

  最近需要做一个客户端程序,需要考虑UI,简单说就是需要做的炫,Electron是一个比较好的选择。由于这个程序需要连接串口的RFID读写器,所以故事就开始了。

  • 二、搭建环境
  • 安装Node.js

    https://nodejs.org/en/ 官网下载安装,安装node.js会自动安装npm。

    NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:

    • 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
    • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
    • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

    由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。这时候我们检查一下node.js和npm是否安装成功。

    

    node.js 和 npm 有版本对应关系,虽然说捆绑安装一般不会有问题,但最好还是确认一下,避免掉坑。可以到官网查看:https://nodejs.org/zh-cn/download/releases/ 

    

  • 安装electron

    有了npm安装 electron就简单了。直接全局安装:npm install -g electron。更多详情参照官网:https://electronjs.org/docs/tutorial/installation

    electron 提供一个初始的项目,可以去github克隆,地址:https://github.com/electron/electron-quick-start。安装完成后同样检查一下是否安装成功。

    

  • 使用node-serialport

    首先从 https://github.com/electron/electron-quick-start 克隆一个 electron-quick-start。然后cd到这个目录。运行electron . 查看是否能正常运行,顺便也可以检验一下我们的环境搭建是否成功。

    

    添加 node-serialport 读写串口的代码

    main.js源码:

// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('path')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow() {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('index.html')

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') app.quit()
})

app.on('activate', function () {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) createWindow()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
var SerialPort = require('serialport')
var sp = new SerialPort('COM2', { baudRate: 9600 })

function writeport(senddata){
  sp.write(senddata, function (err) {
        if (err) {
            return console.log('Error on write: ', err.message);
        }
        console.log('send: ' + senddata);
    });
}

function openport(event){
  sp.on('open', function () {
    console.log("open port success.");
  });

  // open errors will be emitted as an error event
  sp.on('error', function (err) {
    console.log('Error: ', err.message);
  })

  sp.on('data', function (data) {
    //hex
    console.log('recv: ' + data.toString('hex'));
    //ascii
    //console.log('recv: ' + data.toString('ascii'));
    event.sender.send('asynchronous-reply', data.toString('hex'))
  });
}

function closeport(){
  sp.on('close', function () {
    console.log("open port success.");
  });
}

const { ipcMain } = require('electron')
ipcMain.on('asynchronous-message', (event, cmd) => { //主进程接收渲染进程的请求事件
  console.log("read_serialport")
  switch (cmd) {
    case "read_serialport":
      openport(event)
      break;
  }
});

renderer.js源码:

// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// No Node.js APIs are available in this process because
// `nodeIntegration` is turned off. Use `preload.js` to
// selectively enable features needed in the rendering
// process.

// 获取按钮和容器的DOM节点
var content = document.getElementById('content');
var button = document.getElementById('btn');

const ipcRenderer = require('electron').ipcRenderer;

ipcRenderer.on('asynchronous-reply', (event, arg) => {
    content.innerText = arg;
})

button.addEventListener('click', (e) => {
    ipcRenderer.send('asynchronous-message', 'read_serialport')
});

index.html源码:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<button type="button" id="btn">读取串口</button>
<div>串口数据:</div>
<div id="content"></div>

<script src="./renderer.js"></script>
</body>
</html>

  接下来的任务就是这么让这个代码在eletron环境中跑起来。先到serialport官网看看serialport文档 https://serialport.io/docs/guide-about其中有在electron中使用的说明:

  

   如果需要serialport作为Electron项目的依赖项,则必须针对项目使用的Electron版本对其进行编译。这就是爬坑的开始......  

  (1) 命令行窗口 cd 到我们的electron-quick-start项目目录。执行npm install 安装依赖包一切顺利。

  (2) 执行 npm install serialport 下载 serialport 包。不过下载下来的是C++源码,需要我们编译。不详的预感越来越强烈。既然需要编译源码。那么就需要安装node-gyp包。

  npm 为了方便干脆就直接源码分发,用户装的时候再现场编译。 因为node程序中需要调用一些其他语言编写的 工具 甚至是dll,需要先编译一下,否则就会有跨平台的问题,例如在windows上运行的软件copy到mac上就不能用了,但是如果源码支持,编译一下,在mac上还是可以用的。node-gyp在较新的Node版本中都是自带的。

  关于更多 node-gyp的信息请移步https://github.com/nodejs/node-gyp/,一般会自带,node-gyp -v 看一下,如果没有就安装以下,直接 npm install -g node-gyp 一般不会出什么状况。

  node-gyp 需要依赖其他环境,具体参阅 https://github.com/nodejs/node-gyp/中的 Installation

  

   接下来安装 electron-rebuild 。其作用就是根据您的Electron项目使用的Node.js版本重建本机Node.js模块。这样,您就可以在Electron应用程序中使用本机Node.js模块,而无需与系统版本的Node.js完全匹配。参见官网: https://github.com/electron/electron-rebuild 。

  npm install --save-dev electron-rebuild 这里说明一下 -save-dev -save 的意思是将模块安装到项目目录下,-save-dev 的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖

  到这里,准备工作就好了,进入关键的编译。执行 :  .\node_modules\.bin\electron-rebuild.cmd 开始编译,如果一切顺利编译成功,任务完成。但是很显然我没有编译成功,不然也不会在这里啰嗦一大推。

  

   这个地方报错说没有重载接收2个参数。用Visual studio 打开代码 electron-quick-start项目目录下的 node_modules\@serialport\bindings\build\bindings.vcxproj。编译发现确实少了参数。

  

  然而定义是这个样子的:

  

   到这里我第一反应是我安装模块的版本不对,从头到尾折腾了一遍,前面我们安装的那一大推,都重新安装一遍,重新更新一遍发现还是问题依旧,内心十分崩溃。确定前面安装的都是最新版本了,那么问题应该是出在serialport代码上,于是乎去github下载了最新代码与npm install 下载的进行对比,果然发现了问题所在。

  

  然后将github下载的源码覆盖到 electron-quick-start项目目录下的 node_modules\@serialport 再次执行 :  .\node_modules\.bin\electron-rebuild.cmd 。顺利编译成功!

    

  • 最后

   这个应该是属于版本更新延迟的问题!  记录一下避免自己再次坑,如果能帮到其他人我很荣幸~ !

12-13 14:00
查看更多