前言
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时所遇到的问题以及所找到的解决办法,希望对大家有所帮助。若有不足或错误的地方,欢迎指出~