目录结构
- electron-main:主进程目录
- electron-preload:预加载代码目录,主要是定义桥接通信
- 其他文件:也就是 vue 初始化后的目录
安装electron
npm install -D electron electron-builder rimraf vite-plugin-electron electron-devtools-installer
生成应用图标
npm i electron-icon-builder --D
- electron-builder:打包工具
- rimraf:快速删除文件或目录工具
- vite-plugin-electron:vite 结合 electron 的库,关于这个插件可以参见 Vite 与 Electron 无缝衔接
- electron-devtools-installer:electron 开发工具
IPC通信
IPC(Inter-Process Communication),就是进程间通信。Electron应用程序区分主进程和渲染进程,有时候,两者之间需要通信,传输一些数据、发送一些消息。
渲染进程 TO 主进程
渲染进程通过ipcRenderer send 方法发送一个消息管道名称
主进程通过ipcMain on 接收消息 ,第一个参数为消息管道名称,第二个为回调函数
主进程 TO 渲染进程
主进程通过渲染进程(BrowserWindow实例)的webContents send 方法发送 第一个参数任务名称 第二个参数内容
渲染进程通过ipcRenderer on 接受消息, 第一个参数为任务名称,第二个为回调函数
渲染进程 TO 渲染进程
方法一:渲染进程把消息发给主进程,再通过主进程发给对应的渲染进程
方法二:通过ipcRenderer sendTo方法,前提是知道接受方的webContents的id
ipcRenderer.sendTo(webContentsId, channel, ...args)
渲染进程使用require
创建窗口时配置webPreferences的nodeIntegration: true和contextIsolation: false
Preload TO 渲染进程
preload中通过 ipcRenderer.send 把消息发给主进程,主程序再去做处理。
常用api
BrowserWindow 浏览器对象
attribute
尺寸相关
height、width、x、y、minHeight、minWidth、maxHeight、maxWidth
frame 是否无菜单栏(默认:false) boolean:true/false
alwaysOnTop 是否置于顶层(默认:false) boolean:true/false
resizable 是否可改变窗口大小(默认:true) boolean:true/false
webPreferences.nodeIntegration(默认:false) 是否集成node boolean:true/false
webPreferences.webSecurity(默认:true) 是否开启浏览器跨域 boolean:true/false
webPreferences.preload 预加载脚本注入 string:接受一个路径作为参数
api
setAlwaysOnTop 窗口位于顶层
setBounds 设置窗口位置 (x:number , y:number, width: number,height:number)
close 关闭窗口
show 显示窗口
hide 隐藏窗口
loadFile 加载本地应用 (可以传入hash、query、search)
loadURL 加载URL应用
本地开发项目URL格式http://{process.env['VITE_DEV_SERVER_PORT']}
窗口状态
isMaximized 是否最大化
isMinimized 是否最小化
isVisible 是否可见(最小化也是true)
常用构造类
Tray 托盘构造类 (传入图标路径)
setToolTip 鼠标移到托盘显示的文字
Menu 菜单构造类
buildFromTemplate 定制菜单 (传入菜单子项数组集)
Notification 通知栏构造类
参数:
title : 标题
body: 内容
icon: 图标
node-abi检测
使⽤node-abi来获取electron和node对应的abi版本
使用相同的abi版本会更加稳定
生命周期
APP相关
app.on('ready', callback)
应用程序准备就绪,通常在这个事件中创建BrowserWindow
app.on('web-contents-created', callback)
有新窗口(BrowserWindow )创建触发, 可以给窗口绑定事件
app.on('window-all-closed', callback)
所有窗口都关闭,可以在此触发程序退出
app.on('before-quit',callback)
程序退出前,通常在这个事件中卸载监听
app.on('quit', callback)
程序退出
BrowserWindow 相关
BrowserWindow.on('close', callback)
窗口关闭
BrowserWindow.on('show', callback)
窗口显示
BrowserWindow.on('hide', callback)
窗口隐藏
BrowserWindow.on('resized', callback)
窗口改变大小
BrowserWindow.on('maximize', callback)
窗口最大化
BrowserWindow.on('unmaximize', callback)
窗口取消最大化
BrowserWindow.on('ready-to-show', callback)
窗口页面已经渲染完成,窗口先设置 show:false,在这个事件里设置show:true
功能实现
托盘闪烁
通过定时器,间隔切换托盘的图标,原图标> <透明图标(nativeImage.createEmpty())
条码扫描
在渲染应用中监听keypress事件(只适用窗口显示才能触发)
系统级键盘(iohook)监听,通过监听系统级的keypress事件达到条码内容获取,把内容通过IPC通信发给渲染应用。(未检测到360安全守卫拦截)
华视读卡器
通过ffi-napi 包加载Termb.dll文件(同级目录还需要包含WltRS.dll),配置api方法
通过调用CVR_InitComm 初始化,传入iPort号(默认:1001) 定时调用CVR_Authenticate 认证卡片,认证成功(即值:1), CVR_Read_FPContent 读取卡片内容,通过GetPeopleName和GetPeopleIDCode获取卡片的姓名和身份证号码
窗口右键菜单
Electron 是禁用窗口的右键功能 ,可能是处于安全的考虑。
在页面中监听 window.addEventListener('contextmenu') 事件 ,发起IPC通讯通知主程序创建对应的Menu,执行menu.popup方法传入对应的窗口对象。
webview页面中需要脚本植入的方式监听
webviewObject.executeJavaScript(window.addEventListener('contextmenu));
热更新(自动更新)
引入 electron-updater
import { autoUpdater } from 'electron-updater';
常用API:
设置软件包更新地址
autoUpdater.setFeedURL({
provider: 'generic',
url: 192.xxx.xxx.xx
});
设置 退出时是否自动安装 默认为true
autoUpdater.autoInstallOnAppQuit = false;
是否自动下载安装包
autoUpdater.autoDownload = false;
检查是否有新的安装包
autoUpdater.checkForUpdates();
手动触发下载安装包
autoUpdater.downloadUpdate().then(() => {
// do some
}).catch(e =>
// do some
)
取消下载
autoUpdater.updateCancelled()
手动触发 退出程序并安装
autoUpdater.quitAndInstall()
在package.json build 增加
url为更新包服务器地址
本地测试需要添加如下
主进程更新: 全量更新
渲染进程更: 增量/全量更新
增量免安装,下载资源文件进行替换
检查版本
更新资源替换
版本管理及回滚
版本号使用参照以下规则:
主要版本:例如从0.3.5升级到1.0.0,需要升级应用程序,进行一次全量更新
次要版本:从0.3.5升级到0.3.6 小版本更新不涉及主进程更改,进行一次增量更新
版本回滚
配置allowDowngrade 属性为true即可检测版本回退
开机自启动
调用 app.setLoginItemSettings() 函数,传入openAtLogin:true(true:开启,false:关闭)
应用打包安装
图标相关
应用图标可通过 electron-icon-builder 打包输出
build: "win": {"icon": "xxxxxx"}
默认会找项目根路径上build/icons下的图标
安装、卸载、安装页面头图标 :如图依次对应 (默认会找build/对应配置名.ico)
package.json中的nsis配置
一般build为,可以自动配置图标
安装用户协议
package.json中的nsis配置
用户协议可选择 txt文件(编码要ANSI,不然会乱码)和html文件(链接跳转 加上target=_blank)
安装界面流程自定义
NSIS脚本
package.json中的nsis配置
注册表写入、安装配置表单填写等(里面的语音和配置比较多,不细写)
include 适用于相对简单的安装界面
script 可以制作成相对复杂的安装界面
通过 NSIS
加密及应用证书签名
'signingHashAlgorithms':代表加密的方式,一般分为'sha256'与'sha1'两种方式
'rfc3161TimeStampServer':代表时间戳,一般使用'timestamp.digicert.com'
'certificateFile':证书的地址,必须位pfx格式
'certificatePassword':证书的私钥密码,这个在配置证书的时候进行设置
签名导出
以数安时代为例,也可以用签名狗(U盘)
把证书导出为cer格式,然后修改后缀为pfx
优缺点
Q:应用体量大,需要包含chromeium内核,每个窗口都要创建BrowserWindow对象,内存占用率大
兼容性
不支持XP
mac去掉内部API(封杀electron)
踩的坑
- 运行electron-builder时进行到downloaded 会卡住然后失败
解决: 根目录增加文件.npmrc配置镜像源,内容
ELECTRON_MIRROR=npm.taobao.org/mirrors/ele…
ELECTRON_BUILDER_BINARIES_MIRROR=npm.taobao.org/mirrors/ele…
- Application entry file "index.js" in the......................................................
解决: 删除package.json 的files选项,在顶部增加"main": "dist/electron-main/index.js" ,dist为打包的文件夹名。
- 悬浮球拖拽出现阴影底色
解决:初始化窗口添加参数 hasShadow:false 不显示阴影
- require is not defined
解决:初始化BrowserWindow时,需要配置参数
webPreferences:{
nodeIntegration: true,
contextIsolation: false,
}
- Error: contextBridge API can only be used when contextIsolation is enabled
使用preload 预加载脚本时,报上面的错误
解决:初始化BrowserWindow时,需要修改配置参数
webPreferences:{
contextIsolation: true,
}
和require的使用互斥,使用node需要把contextIsolation配置成false,就无法使用预加载脚本。
- 使用 iohook监听系统级鼠标键盘事件,不兼容
环境:node: v16.15.0 abi: 93
electron: v18.2.0 abi: 103
package.json
运行npm install iohook -D
报错信息:
解决:
iohook官方文档显示版本支持情况
查看iohook的github版本情况
最高支持node 到 88 abi 对应版本为15.x.x
通过nvm版本管理把node版本降到14.18.1 32-bit(对齐第六点)
npm i iohook -S
出现下面报错(npm config registry 已经配置成registry.npm.taobao.org/)
原因是请求github.com 一直请求不上或者慢
打开ping.chinaz.com/github.com,…
找一个响应时间快的,复制ip地址
打开 C:\Windows\System32\drivers\etc\hosts 文件
复制一份,放在桌面添加内容(直接编辑会权限不够),拖动直接替换原文件(前面是ip地址)
打开控制台,输入ping github.com ,出现的ip地址就是配置的
接着安装出现下面问题
去github issues 找到对应问题
查看github releases
未找到node-v83-linux-x32的版本包 (83是node版本对应的abi)
因为应用没有打包到linux环境的需求,所以暂时把linux从platforms 去掉
old:
new:
- 安装ffi-napi 集成dll 出现版本兼容问题,python以及node-gyp 版本错误
环境:node16.15.0
解决:删除现有node包,安装nvm多版本管理node,把node版本包降到14.18.1
运行npm i -g windows-build-tools npm install -g node-gyp
7.使用ffi-napi 出现Dynamic Linking Error: Win32 Error 126错误
dll文件路径问题 找不到指定的模块
- 使用ffi-napi 出现 Dynamic Linking Error: Win32 error 193
dll文件和node版本不一致,32位或者64位
解决:通过nvm管理node版本,安装32位node包
9.调用华视读卡器Termb.dll 初始化CVR_InitComm 报 Could not get SDT_OpenPort address
缺少dll文件,sdtapi.dll 、WltRS.dll、DLL_File.dll
下载地址:www.chinaidcard.com//uploadfile…
- 控制台中文乱码
解决:运行时加上chcp 65001
- 本地调试electron-updater 新版本无法更新
原因:electron-updater在本地调试时会去取electron的版本,而不是app的版本(bug)
解决:添加如下判断,应为currentVersion是只读属性,故需要劫持属性get方法
config 为package.json
- electron使用axios报错
需要添加即可
- electron 使用axios 报错
ERR_TLS_CERT_ALTNAME_INVALID
接口证书过期
引入 https包,配置httpAgent 忽略TLS证书校验
- webview中的页面无法触发window.open打开新窗口
在webview标签内增加 allowpopups webpreferences="nativeWindowOpen=yes" 属性
- 请求TSL1.0的服务器接口报 SSL错误
使用https库转发请求