一、按照慣例先說點廢話
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。編譯發現確實少了參數。
然而定義是這個樣子的:
從github下載了node-seriaport最新代碼與npm install 下載的進行對比,果然發現了問題所在。
這時候我又在node環境測試了一遍,發現node-serialport模塊是能正常工作的。說明我npm install 下載的模塊版本有問題。打開packge.json看了一下,發現里面依賴的版本的7.1.5。
而node環境里面可以正常工作的版本是 8.0.6 (可以通過 npm list [module name] 查看模塊版本號)。然后我把修改了pack.json里面依賴的serialport版本,刪除packge-lock.json和node_modules。(執行npm install 重新生成packge-lock.json ,如果沒有生成 修改一下配置 npm config set package-lock true) 重新執行前面的編譯過程。順利編譯成功:
- 最后
折騰的過程很痛苦! 記錄一下避免自己再次坑,如果能幫到其他人我很榮幸~ !