Electron 初探

1,360 阅读8分钟

目录结构

  • 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

创建窗口时配置webPreferencesnodeIntegration: truecontextIsolation: 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[VITEDEVSERVERHOST]:{process.env['VITE_DEV_SERVER_HOST']}:{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方法

image.png

通过调用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为更新包服务器地址

image.png

本地测试需要添加如下

image.png

主进程更新: 全量更新

渲染进程更: 增量/全量更新

增量免安装,下载资源文件进行替换

检查版本

image.png

更新资源替换

image.png

版本管理及回滚

版本号使用参照以下规则:

主要版本:例如从0.3.5升级到1.0.0,需要升级应用程序,进行一次全量更新

次要版本:从0.3.5升级到0.3.6 小版本更新不涉及主进程更改,进行一次增量更新

版本回滚

image.png

配置allowDowngrade 属性为true即可检测版本回退

开机自启动

调用 app.setLoginItemSettings() 函数,传入openAtLogin:true(true:开启,false:关闭)

应用打包安装

图标相关

应用图标可通过 electron-icon-builder 打包输出

build: "win": {"icon": "xxxxxx"}

默认会找项目根路径上build/icons下的图标

安装、卸载、安装页面头图标 :如图依次对应 (默认会找build/对应配置名.ico)

package.json中的nsis配置

image.png

一般build为,可以自动配置图标

image.png

安装用户协议

package.json中的nsis配置

用户协议可选择 txt文件(编码要ANSI,不然会乱码)和html文件(链接跳转 加上target=_blank)

image.png

安装界面流程自定义

NSIS脚本

package.json中的nsis配置

image.png

注册表写入、安装配置表单填写等(里面的语音和配置比较多,不细写)

include 适用于相对简单的安装界面

script 可以制作成相对复杂的安装界面

通过 NSIS

加密及应用证书签名

'signingHashAlgorithms':代表加密的方式,一般分为'sha256'与'sha1'两种方式

'rfc3161TimeStampServer':代表时间戳,一般使用'timestamp.digicert.com'

'certificateFile':证书的地址,必须位pfx格式

'certificatePassword':证书的私钥密码,这个在配置证书的时候进行设置

image.png

签名导出

以数安时代为例,也可以用签名狗(U盘)

image.png

把证书导出为cer格式,然后修改后缀为pfx

优缺点

image.png

Q:应用体量大,需要包含chromeium内核,每个窗口都要创建BrowserWindow对象,内存占用率大

兼容性

不支持XP

mac去掉内部API(封杀electron)

踩的坑

  1. 运行electron-builder时进行到downloaded 会卡住然后失败

解决: 根目录增加文件.npmrc配置镜像源,内容

ELECTRON_MIRROR=npm.taobao.org/mirrors/ele…

ELECTRON_BUILDER_BINARIES_MIRROR=npm.taobao.org/mirrors/ele…

  1. Application entry file "index.js" in the......................................................

image.png

解决: 删除package.json 的files选项,在顶部增加"main": "dist/electron-main/index.js" ,dist为打包的文件夹名。

  1. 悬浮球拖拽出现阴影底色

解决:初始化窗口添加参数 hasShadow:false 不显示阴影

  1. require is not defined

解决:初始化BrowserWindow时,需要配置参数

webPreferences:{

nodeIntegration: true,

contextIsolation: false,

}

  1. Error: contextBridge API can only be used when contextIsolation is enabled

使用preload 预加载脚本时,报上面的错误

解决:初始化BrowserWindow时,需要修改配置参数

webPreferences:{

contextIsolation: true,

}

和require的使用互斥,使用node需要把contextIsolation配置成false,就无法使用预加载脚本。

  1. 使用 iohook监听系统级鼠标键盘事件,不兼容

环境:node: v16.15.0 abi: 93

electron: v18.2.0 abi: 103

package.json

image.png

运行npm install iohook -D

报错信息:

image.png

image.png

解决:

iohook官方文档显示版本支持情况

image.png

查看iohook的github版本情况

最高支持node 到 88 abi 对应版本为15.x.x

image.png

通过nvm版本管理把node版本降到14.18.1 32-bit(对齐第六点)

npm i iohook -S

出现下面报错(npm config registry 已经配置成registry.npm.taobao.org/)

image.png

原因是请求github.com 一直请求不上或者慢

打开ping.chinaz.com/github.com,…

找一个响应时间快的,复制ip地址

image.png

打开 C:\Windows\System32\drivers\etc\hosts 文件

复制一份,放在桌面添加内容(直接编辑会权限不够),拖动直接替换原文件(前面是ip地址)

image.png

打开控制台,输入ping github.com ,出现的ip地址就是配置的

image.png

接着安装出现下面问题

image.png

去github issues 找到对应问题

image.png

查看github releases

image.png

image.png

未找到node-v83-linux-x32的版本包 (83是node版本对应的abi)

因为应用没有打包到linux环境的需求,所以暂时把linux从platforms 去掉

old:

image.png

new:

image.png

  1. 安装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文件路径问题 找不到指定的模块

  1. 使用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

image.png

下载地址:www.chinaidcard.com//uploadfile…

  1. 控制台中文乱码

解决:运行时加上chcp 65001

  1. 本地调试electron-updater 新版本无法更新

image.png

原因:electron-updater在本地调试时会去取electron的版本,而不是app的版本(bug)

解决:添加如下判断,应为currentVersion是只读属性,故需要劫持属性get方法

config 为package.json

image.png

  1. electron使用axios报错

需要添加即可

image.png

  1. electron 使用axios 报错

ERR_TLS_CERT_ALTNAME_INVALID

image.png

接口证书过期

引入 https包,配置httpAgent 忽略TLS证书校验

image.png

  1. webview中的页面无法触发window.open打开新窗口

在webview标签内增加 allowpopups webpreferences="nativeWindowOpen=yes" 属性

  1. 请求TSL1.0的服务器接口报 SSL错误

image.png

使用https库转发请求

image.png