前言
web端能做的事情很多,但是當涉及到操作系統的時候,可能就有點力不從心了。前段時間在開發一個web系統的時候,就遇到了類似的情況。我們需要獲取電腦操作系統的一些信息,比如mac地址等。我們的web系統是完全放在服務器上,通過瀏覽器來運行的,但是通過web端並不能直接實現我們想要的效果。
問題就是留給人們來解決的。經過同事之間的討論,因爲系統本身並不複雜,而且要進行快速的開發。決定用Electron + 原web系統的頁面,來解決涉及操作系統信息的問題。
這篇文章總結了我在使用Electron 時所遇到的一些問題和解決方法。
什麼是Electron?
使用 JavaScript, HTML 和 CSS 構建跨平臺的桌面應用——這是Electron官網的簡介
Electron是GitHub開發的一個開源框架。它允許使用Node.js(作爲後端)和Chromium(作爲前端)完成桌面GUI應用程序的開發。Electron現已被多個開源Web應用程序用於前端與後端的開發,著名項目包括GitHub的Atom和微軟的Visual Studio Code。——知乎
可以簡單的理解爲Electron爲web項目套上了Node.js環境的殼,使得我們可以調用Node.js的豐富的API。這樣我們可以用JavaScript來寫桌面應用,拓展很多我們在web端不能做的事情。
怎麼構建Electron應用?
構建Electron應用很簡單,可以直接查看官方文檔,也可以利用現有的輪子。基本來說可以分爲兩大類:模板和命令行工具。這兩種方式都有各自的優點,具體選用哪種方式要根據自己實際的情況來。就拿我來說,因爲我要做的項目原本就有web端的頁面了,這些模板基本都不適用,爲了趕進度,就直接參考官網入門——打造你的第一個-electron-應用,再加上原有的代碼進行構建項目。
下面爲一些常用的構建模板與命令行工具
模板
- electron-react-boilerplate:electron + react
- electron-vue: electron + vue
- …
命令行工具
- 打造你的第一個-electron-應用:官網教程
- electron-forge:一個用來構建現代化Electron應用的完善的工具
- …
一個例子
下面是一個最基礎的Electron項目,後續的代碼都是在此基礎上進行拓展。
npm install --save-dev electron
一般結構
demo/
├── package.json
├── main.js
└── index.html
package.json
{
"name": "demo",
"version": "1.0.0",
"description": "electornDemo",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"author": "xmanlin",
"license": "MIT",
"devDependencies": {
"electron": "^9.0.0"
}
}
main.js
const {app, BrowserWindow} = require('electron')
app.on('ready', function createWindow () {
// 可以創建多個渲染進程
let win = new BrowserWindow({
width: 800,
height: 600,
})
win.show()
// 渲染進程中的web頁面可以加載本地文件
win.loadFile('index.html')
// 記得在頁面被關閉後清除該變量,防止內存泄漏
win.on('closed', function () {
win = null
})
})
// 頁面全部關閉後關閉主進程,不同平臺可能有不同的處理方式
app.on('window-all-closed', () => {
app.quit()
})
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
</head>
<body>
<div>一個electron項目</div>
<script>
</script>
</body>
</html>
什麼是主進程和渲染進程?
在Electron中,主進程和渲染進程的概念是十分重要的,具體可以查看官網介紹:主進程和渲染進程。
主進程
- 運行
package.json
中的main
腳本的進程是主進程。 - 一個electron應用有且只有一個主進程。
- 主進程可以進行GUI相關的原生API操作。
渲染進程
- Electron 使用了 Chromium 來展示 web 頁面,所以 Chromium 的多進程架構也被使用到。
- 每個web頁面運行在它自己的渲染進程中。
- 使用
BrowserWindow
類開啓一個渲染進程並將這個實例運行在該進程中,當一個BrowserWindow
實例被銷燬後,相應的渲染進程也會被終止。 - 渲染進程中不能調用原生資源,但是渲染進程中同樣包含Node.js環境,所以可以引入Node.js
模塊,在Node.js支持下,可以在頁面中和操作系統進行一些底層交互。
渲染進程之中如何調用Node.js的API?
在Electron5.0版本後,渲染進程默認是不能調用Node.js的API的,經過設置後纔可以:
主進程(main.js)
let win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true, //設置爲true就可以在這個渲染進程中調用Node.js
}
})
怎樣打開開發者工具?
Electron雖然是用了Chromium,但是想要打開開發者工具並不是像瀏覽器一樣,如在windows下默認情況直接按F12。
在Electron中有兩種方式打開開發者工具:
第一種是在主進程中進行設置,設置後,啓動項目,該渲染進程就默認打開了開發者工具
win.webContents.openDevTools();
第二種可以在渲染進程窗口的菜單欄進行選擇View -> Toggle Developer Tools。(或者直接按照上面的快捷鍵進行操作)
electron 控制檯打印亂碼問題?
我們可能在Windows的控制檯會出現中文亂碼的問題,當我們在Windows的控制檯下輸入chcp,可以查看到當前字符編碼,常見的gb2312的值是936,utf8的值是65001。這種情況下只要對package.json
進行設置就能解決。
"start": "chcp 65001 && electron ."
主進程和渲染進程之間如何通信?
主進程和渲染進程之間可以通過ipcRenderer 和 ipcMain模塊通信。
主進程主動向渲染進程發送消息
主進程(main.js)
//主進程向渲染進程發送消息,'did-finish-load':當導航完成時發出事件,onload 事件也完成
win.webContents.on('did-finish-load', () => {
win.webContents.send('msg', '消息來自主進程')
})
渲染進程(index.html)
<script>
const {ipcRenderer} = require('electron')
ipcRenderer.on('msg', (event, message) => {
console.log(message) // 消息來自主進程
})
</script>
渲染進程主動向主進程發送消息
渲染進程(index.html)
const {ipcRenderer} = require('electron')
ipcRenderer.send('indexMsg','消息來自渲染進程')
主進程(main.js)
const {ipcMain} = require('electron')
ipcMain.on('indexMsg',(event,msg) => {
console.log(msg) //消息來自渲染進程
})
渲染進程之間如何通信?
渲染進程之間的通信方式有很多種,下面列出幾種:
使用全局共享屬性
//主進程
global.sharedObject = {
user: ''
}
//渲染進程一
const {remote} = require('electron')
remote.getGlobal('sharedObject').user = 'xmanlin'
//渲染進程二
const {remote} = require('electron')
console.log(remote.getGlobal('sharedObject').user) //xmanlin
ipcRenderer.sendTo()
下面是ipcRenderer.sendTo()
的參數
ipcRenderer.sendTo(webContentsId, channel, [, arg1][, arg2][, ...])
ipcRenderer.sendTo(windowId, 'ping', 'someThing')
//webContentsId : Number
//channel : String
//...args : any[]
具體用法
主進程(main.js)
//創建一個新的渲染進程
let win2 = new BrowserWindow({
width: 800,
height: 600,
})
//爲渲染進程設置唯一id
win2.id = 2
渲染進程1
<script>
const {ipcRenderer} = require('electron')
//向id爲2的渲染進程發送消息
ipcRenderer.sendTo(2,'msg1','來自渲染進程1的消息')
</script>
渲染進程2
<script>
const {ipcRenderer} = require('electron')
ipcRenderer.on('msg1', (event, message) => {
console.log(message) // 來自渲染進程1的消息
})
</script>
利用主進程做消息中轉站
//主進程
ipcMain.on('msg1', (event, message) => {
yourWindow.webContents.send('msg2', message);
}
//渲染進程1
ipcRenderer.send('msg1', '來自渲染進程1的消息')
//渲染進程2
ipcRenderer.on('msg2', (event, message) => {
console.log(message) //來自渲染進程1的消息
}
)
如何對項目進行打包?
打包也是必不可少的一步,這裏介紹兩種比較成熟的打包工具:electron-packager和electron-builder。這兩個工具主要是對其進行配置。
electron-packager
我們可以利用 electron-packager把我們現有的electron應用打包爲exe可執行文件。
先進行安裝
npm install electron-packager --save-dev
安裝好之後要配置electron-packager的基本命令,下面爲官方文檔中的基本格式:
electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...]
簡要的介紹一下各個參數所代表的意思:
- sourcedir:項目所在路徑
- appname:應用名稱(打包後文件的名稱)
- platform:確定了你要構建哪個平臺的應用(Windows、Mac 還是 Linux)
platform=win32
代表Windowsplatform=darwin
代表Macplatform=linux
代表Linux
- arch:決定了使用 x86 還是 x64 還是兩個架構都用
- optional options:可選選項
下面爲一個例子,供參考
package.json
"scripts": {
"build32": "electron-packager ./ appDemo --platform=win32 --arch=ia32 --out=./app --app-version=1.0.0 --overwrite --icon=./favicon.ico",
"build64": "electron-packager ./ appDemo --platform=win32 --arch=x64 --out=./app --app-version=1.0.0 --overwrite --icon=./favicon.ico"
}
上面都是打包Windows下的軟件,build32打包的爲32位,build64打包的爲64位。除基本參數外,上面其他參數所代表的意義如下:
- –out表示打包後生成的文件的目錄
- –app-version表示打包生成文件的版本號
- –overwrite表示刪除原有的打包文件,生成新的打包文件
- –icon表示打包文件的圖標
electron-builder
electron-builder不僅可以打包爲exe可執行文件,還可以打包爲可安裝程序,功能與electron-packager相比也要豐富一些。
官網更爲推崇yarn 來進行安裝
yarn add electron-builder --dev
當然npm也是可以的
npm install electron-builder --save-dev
然後我們可以進行配置了
"scripts": {
"pack": "electron-builder --dir",
"dist": "electron-builder"
},
"build": {
"productName": "appDemo", // app中文名稱
"appId": "appDemoId",// app標識
"directories": { // 打包後輸出的文件夾
"buildResources": "resources",
"output": "dist/"
}
"files": [ // 打包後依然保留的源文件
"dist/electron",
"node_modules/",
"package.json"
],
"mac": { // mac打包配置
"target": "dmg",
"icon": "icon.ico"
},
"win": { // windows打包配置
"target": "nsis",
"icon": "icon.ico"
},
"dmg": { // dmg文件打包配置
"artifactName": "appDemo.dmg",
"contents": [
{
"type": "link",
"path": "/Applications",
"x": 410,
"y": 150
},
{
"type": "file",
"x": 130,
"y": 150
}
]
},
"nsis": { // nsis文件打包配置
"oneClick": false,
"allowToChangeInstallationDirectory": true, // 允許修改安裝目錄
"allowElevation": true, // 允許請求提升。 如果爲false,則用戶必須使用提升的權限重新啓動安裝程序。
"installerIcon": "./build/icons/aaa.ico",// 安裝圖標
"uninstallerIcon": "./build/icons/bbb.ico",//卸載圖標
"installerHeaderIcon": "./build/icons/aaa.ico", // 安裝時頭部圖標
"createDesktopShortcut": true, // 創建桌面圖標
"createStartMenuShortcut": true,// 創建開始菜單圖標
"shortcutName": "xxxx", // 圖標名稱
"include": "build/script/installer.nsh", //包含的自定義nsis腳本這個對於構建需求嚴格得安裝過程相當有用。
},
}
在使用electron-builder打包時,也可以指定參數
--mac, -m, -o, --macos macOS打包
--linux, -l Linux打包
--win, -w, --windows Windows打包
--mwl 同時爲macOS,Windows和Linux打包
--x64 x64 (64位安裝包)
--ia32 ia32(32位安裝包)
全部參數可參考Command Line Interface (CLI)
關於NSIS,也可以瞭解一下這種打包方式:electron 打包流程 electron-packager + NSIS
如何設置窗口默認最大化和全屏?
默認最大化
//主進程(main.js)
let win = new BrowserWindow({show: false})
win.maximize()
win.show()
默認全屏
//主進程(main.js)
let win = new BrowserWindow({fullscreen: true})
如何自定義菜單欄?
可以直接參考這篇文章-使用 Electron 自定義菜單,寫的比較詳細,也可以直接參考官方文檔來。當然我們也可以把自帶的菜單欄隱藏掉,然後自己調用寫Electron的API寫一個菜單欄。例如VSCode就是這麼做的,打開VSCode,然後在幫助裏面找到切換開發者工具,你可能會發現新世界~。
如何獲取操作系統mac地址?
這個主要是調用Node.js的API,就可以獲取系統的mac地址。
var os=require("os");
//獲取mac地址
var mac = ''
var networkInterfaces=os.networkInterfaces();
for(var i in networkInterfaces){
for(var j in networkInterfaces[i]){
if(networkInterfaces[i][j]["family"]==="IPv4" && networkInterfaces[i][j]["mac"]!=="00:00:00:00:00:00" && networkInterfaces[i][j]["address"]!=="127.0.0.1"){
mac = networkInterfaces[i][j]["mac"]
}
}
}
參考
https://www.electronjs.org/docs
https://blog.csdn.net/weixin_42762089/article/details/87912222
https://www.jianshu.com/p/62c45eddba4c
https://blog.csdn.net/qq_34803821/article/details/95101595
https://segmentfault.com/a/1190000013924153
https://juejin.im/post/5cfd2ec7e51d45554877a59f
最後
這篇文章是我到目前爲止在學習並應用Electron時所遇到的問題以及所找到的解決辦法,希望對大家有所幫助。若有不足或錯誤的地方,歡迎指出~