0%

electron开发总结

一、用得到的 electron 开发总结

Electron 是由 Github 开发,用 HTML,CSS 和 JavaScript 来构建跨平台桌面应用程序的一个开源库。 Electron 通过将 Chromium 和 Node.js 合并到同一个运行时环境中,并将其打包为 Mac,Windows 和 Linux 系统下的应用来实现这一目的。

添加启动动画

由于 Electron 第一次启动比较慢,需要一些启动动画提高下用户体验。在主进程添加以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
let mainWindow

function createWindow(callback) {
mainWindow = new BrowserWindow({
width: 1400,
height: 900,
show: false
})

mainWindow.once('ready-to-show', () => {
if (callback){
callback();
}
mainWindow.show()
})
mainWindow.loadURL(`file://${__dirname}/build/index.html`);
}
app.on('ready', () => {
const useH = parseInt(0.865 * electron.screen.getPrimaryDisplay().workAreaSize.height);
const useW = parseInt(0.625 * electron.screen.getPrimaryDisplay().workAreaSize.width);

let logoWindow = new BrowserWindow({
width: useW,
height: useH,
transparent: true,
frame: false,
center: true,
show: false
});
logoWindow.loadURL(`file://${__dirname}/build/logo/logo.html`);
logoWindow.once('ready-to-show', () => {
logoWindow.show();
});
const closeLogoWindow = () => {
logoWindow.close();
};
createWindow(closeLogoWindow);
})

实现自动更新

使用 electron-builder 结合 electron-updater 实现自动更新。

配置 package.json 文件

1
2
3
4
5
6
7
8
"build": {
"publish": [
{
"provider": "generic",
"url": "http://ossactivity.tongyishidai.com/"
}
]
}

主进程添加自动更新检测和事件监听:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { autoUpdater } from "electron-updater"
import { ipcMain } from "electron"

function updateHandle() {
let message = {
error: '检查更新出错',
checking: '正在检查更新……',
updateAva: '检测到新版本,正在下载……',
updateNotAva: '现在使用的就是最新版本,不用更新',
};

autoUpdater.setFeedURL('http://ossactivity.tongyishidai.com/');
autoUpdater.on('error', function (error) {
console.log(error)
sendUpdateMessage(message.error)
});
autoUpdater.on('checking-for-update', function () {
sendUpdateMessage(message.checking)
});
autoUpdater.on('update-available', function (info) {
sendUpdateMessage(message.updateAva)
});
autoUpdater.on('update-not-available', function (info) {
sendUpdateMessage(message.updateNotAva)
});

// 更新下载进度事件
autoUpdater.on('download-progress', function (progressObj) {
mainWindow.webContents.send('downloadProgress', progressObj)
})
autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {

ipcMain.on('isUpdateNow', (e, arg) => {
console.log(arguments);
console.log("开始更新");
//some code here to handle event
autoUpdater.quitAndInstall();
});

mainWindow.webContents.send('isUpdateNow')
});

ipcMain.on("checkForUpdate",()=> {
//执行自动更新检查
autoUpdater.checkForUpdates();
})
}

// 通过main进程发送事件给renderer进程,提示更新信息
function sendUpdateMessage(text) {
mainWindow.webContents.send('message', text)
}

注意:

  • 在添加自动更新检测和事件监听之后,在主进程 createWindow 中需要调用一下 updateHandle()。
  • 这个 autoUpdater 不是 electron 中的 autoUpdater,是 electron-updater 的 autoUpdater。(这里曾报错曾纠结一整天)

在视图(View)层中触发自动更新,并添加自动更新事件的监听。

触发自动更新:

1
ipcRenderer.send("checkForUpdate");

监听自动更新事件:

1
2
3
4
5
6
7
8
9
10
11
12
ipcRenderer.on("message", (event, text) => {
console.log(text)
});
ipcRenderer.on("downloadProgress", (event, progressObj)=> {
console.log(progressObj.percent)
this.setState({
downloadPercent: parseInt(progressObj.percent) || 0
})
});
ipcRenderer.on("isUpdateNow", () => {
ipcRenderer.send("isUpdateNow");
});

为避免多次切换页面造成监听的滥用,切换页面前必须移除监听事件:

1
ipcRenderer.removeAll(["message", "downloadProgress", "isUpdateNow"]);

参考地址 segmentfault.com/a/119000001…

使用 electron-builder 打包 exe 安装包

package.json 文件配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{
"build": {
"productName": "CubeScratch",
"appId": "org.develar.CubeScratch",
"publish": [
{
"provider": "generic",
"url": "http://ossactivity.tongyishidai.com/"
}
],
"win": {
"icon": "icon.ico",
"artifactName": "${productName}_Setup_${version}.${ext}",
"target": [
"nsis"
]
},
"mac": {
"icon": "icon.icns"
},
"nsis": {
"oneClick": false,
"allowElevation": true,
"allowToChangeInstallationDirectory": true,
"installerIcon": "icon.ico",
"uninstallerIcon": "icon.ico",
"installerHeaderIcon": "icon.ico",
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "CubeScratch"
},
"directories": {
"buildResources": "resources",
"output": "release"
},
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
}
}
}

参考地址 juejin.im/post/684490…

windows 平台 serialport 串口编译

如果需要 serialport 作为 Electron 项目的依赖项,则必须针对项目使用的 Electron 版本对其进行编译。
对于大多数最常见的用例(标准处理器平台上的 Linux,Mac,Windows),我们使用 prebuild 来编译和发布库的二进制文件。
使用 nodejs 进行编译 node-gyp 需要使用 Python 2.x,因此请确保已安装它,并且在所有操作系统的路径中。Python 3.x 无法正常工作。
安装 windows 构建工具和配置想省时省力请选择以下方案:

1
npm install --global --production windows-build-tools

上边命令决定串口编译是否成功。安装过程非常缓慢,安装完成就等于串口编译成功了 99%。
手动安装工具和配置请看https://github.com/nodejs/node-gyp/
接下来用 electron-rebuild 包重建模块以适配 Electron。这个包可以自动识别当前 Electron 版本,为你的应用自动完成下载 headers、重新编译原生模块等步骤。

1
2
3
"scripts": {
"rebuild": "electron-rebuild"
}

执行以下命令完成串口编译

1
npm run rebuild

注意:

  • 编译过的串口不同系统不可通用,需在各平台重新编译
  • windows 系统最好是正版,或净化版。否正很有可能安装失败。

二、Electron:使用 React 作为 Renderer

1. 前言

通过之前对 Electron 的了解,现在多少也能看出 Electron 的一些特点。

其中,Main Process 用来调度各个 Renderer Process;而各个 Renderer Process 实际上就是 web+(比 web 功能要强,姑且就允许我这么叫一次吧),他们除了 web 应用本身所拥有的特征之外,还可以通过引入 Electron 来扩展,当然也可以使用 NodeJs 的特性,比如在 React 中使用 fs.writeFile()(听起来很诡异)。

如果之前的意见记不清楚了的话,可以再去翻翻看《Electron:Web 应用桌面化》、《Electron:主进程与渲染器进程》。

本篇呢,主要是为了来总结一下如何将 React 与 Electron 结合起来,让编码更加高效。

2. 为什么要引入 React ?

实际上在最初学习的时候直接在 Electron 的应用中写 html+js+css 感觉也不错,但是当场景稍微复杂起来后就会感觉写起来比较繁琐。

另外还有就是在切换页面的场景,如果复用渲染器进程而直接采用切换 Url 的方式虽然也可以完成导航,但是会有一个“白屏时间”让人挺不舒服的。如果每个页面都起一个自己的渲染器进程,由于新窗口的启动有一个过渡,因此不会感觉到“白屏时间”,但是总感觉每个页面都起一个渲染器进程也并不是适用于任意位置。

所以说 SPA 是非常合适的一个选择,至于选什么框架并不重要。

最基础的整合

通过分别在 Electron 中添加 React 和在 React 中添加 Electron 作比较之后,我发现在 React 中添加 Electron 仿佛要简单一些(也可能目前场景比较简单 🤔)。

新建一个 React App

1
yarn create react-app electron-react

结束之后跑起来这个站点,假设站点地址是 http://localhost:3000,成功执行就可以了,保持这个执行状态不用关闭。

添加 Electron

1
yarn add electron --dev

在 src/ 目录下新建 main.js 入口文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/main.js
const { app, BrowserWindow } = require('electron')

app.allowRendererProcessReuse = true

function createWindow () {
let win = new BrowserWindow({
height: 500,
width: 800,
webPreferences: {
nodeIntegration: true
}
})

win.loadURL(`http://localhost:3000`)
}

app.on('ready', () => createWindow())

之后修改 package.json 文件,改一下入口文件,加一个 script:

1
2
3
4
"main": "src/main.js",
"scripts": {
"start:main": "electron ."
}

执行

1
yarn start:main

不出意外应该是成功了的。

在 React 中使用 Electron

默认在 React 中 使用 require(‘electron’) 是不行的,会报错:

image

报错的原因是通过 create-react-app 创建的应用中 target 是 web 环境 ,因此不能识别 node api。

下面通过两种途径可以来解决这个问题。

1. 绕过 Webpack 的检查机制解决

比较简单的处理办法就是使用 window.require('electron') 代替 require('electron')。window 对象在 electron 中是指向 global 的,所以它在执行时可以找到 require 函数。

2. 修改 React Webpack 配置解决

为了验证上面报错原因的猜测,我尝试 eject 了一个项目去观察,配置中的 target 确实是缺失的,那么理论上把这个 target 修改为对应的 target 就可以支持 require('electron') 了。

你可以点击这里来查看 target 都有哪些

显然我们需要的是:electron-renderer

为了修改这个 target,首先需要把 React 项目 eject

1
npm run eject

Note: this is a one-way operation. Once you eject, you can’t go back!(此过程不可逆,请谨慎操作!)

弹出后,打开 config/webpack.config.js 文件,大约在 130 行左右,添加代码片段中标记的那一句,我截取了一小段代码:

1
2
3
4
5
6
7
return {
target: 'electron-renderer', // <- 添加这一句
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap

好了,重新启动后 require('electron') 就生效了,值得注意的是 无法直接在 Chrome 中运行,必须从 Electron 中打开。

取消 React 默认打开浏览器的行为

方法 1

  • bash:

bash 可能还是比较常用的一个 terminal

1
2
3
4
5
6
7
8
9
# 设置临时环境变量
BROWSER=none
# 执行
yarn start:render

#or

# 连续命令
BROWSER=none && yarn start:render
  • cmd:

如果你用的是 cmd,你可以使用下面的方式

1
2
3
4
5
6
7
8
9
# 设置变量
set "BROWSER=none"
# 执行
yarn start:render

# or

# 连续命令
set "BROWSER=none" && yarn start:render

设置临时环境变量只需要一次就可以了,该变量只在当前打开的 cmd 中有效。

  • pwsh:

power shell 与 cmd 有所不同

1
2
3
4
5
6
7
8
9
# 设置变量
$env:BROWSER="none"
# 执行
yarn start:render

# or

# 连续命令(我这里尝试会卡住)
($env:BROWSER="none") -and (yarn start:render)

方法 2

在根目录添加一个 .env 文件:

1
2
// .env
BROWSER=none

之后正常执行就可以。

3. 打包 🛠️

没有打包功能的 Electron App 就没有意义,毕竟软件做出来是给人用的 🙃。

Electron 方面推荐有两个打包工具:

  • electron-forge
  • electron-builder
    相对来讲,electron-forge 更加适合于从零开始,直接基于 electron-forge 提供的模板项目开始开发 electron 应用。而 electron-builder 适合于对已有项目的打包。

相信一般来到这里的大家或者我自己也是,已经准备好了一个项目,就差打包了,所以大家一般更倾向于使用 electron-builder。

安装 electron-builder

1
yarn add --dev electron-builder

配置 electron-builder

在根目录创建一个 electron-builder.yml 的配置文件,用于配置 electron-builder 打包相关内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
appId: com.example.app
copyright: ©2020 bey6
productName: bey6
# asar 加密
asar: false
extends: null

# 目录
directories:
buildResources: assets/
output: dist/

files:
- package.json
- build/
- node_modules/
- src/main.js

dmg:
contents:
- type: link
path: /Applications
x: 410
y: 150
- type: file
x: 130
y: 150
win:
# 目标类型
target: nsis

nsis:
oneClick: false
# 允许修改安装路径
allowToChangeInstallationDirectory: true
# 安装给所有用户
perMachine: true

修改 package.json

  • 修改 homepage
  • scripts 中添加一个打包 win64 的脚本
1
2
3
4
"homepage": ".",
"scripts": {
"build:win64": "yarn build && electron-builder --win --x64"
},

修改 Main Process

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/main.js
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')

app.allowRendererProcessReuse = true

function createWindow () {
let win = new BrowserWindow({
height: 500,
width: 800,
webPreferences: {
nodeIntegration: true
}
})

// 修改这里
if (process.env.NODE_ENV === 'dev') win.loadURL(`http://localhost:3000`)
else win.loadFile(path.resolve(__dirname, '../build/index.html'))
}

// ...

打包

1
yarn build:win64

打包结束后会在根目录多出来一个 dist/ 目录,执行其中的 .exe 即可完成安装。