0%

electron应用


讲electron之前先说下 B/S、C/S
虽然B/S是目前开发的主流,但是C/S仍然有很大的市场需求

C/S 即Client/Server,客户端/服务器 架构,主要应用于局域网内
是一种典型的两层架构。客户端:用户程序(表示层) 服务器端:一种是数据库服务器,一种是Socket服务器。(数据库层)

B/S 即Browser/Server,浏览器/服务器 架构,主要应用于广域网中
三层:Browser客户端、webapp服务器端和DB(数据库)端。

什么是electron

Electron是一个基于Chromium和 Node.js,使用 HTML、CSS和JavaScript来构建跨平台应用的跨平台开发框架,

技术组成:Electron = Chromium + Node.js + Native API

兼容 Mac、Windows 和 Linux。

目前,Electron已经创建了包括 VScode 和 Atom 在内的大量应用

简单来说就是Electron是构建桌面应用(区别手机应用)的,再直白点比如电脑的酷狗音乐,在windows和mac上都能用

桌面端的开发方式主要有 Native 、 QT 、 Flutter 、 NW 、 Electron 、 Tarui

其各自优劣势如下表格
| 选型 | 性能 | 包体积 | 安全 | 迭代速度 | 跨平台 | 生态和社区 |
| :—— | :—— | :—— | :—— | :—— | :—— | :—— |
| Native | 高 | 小 | 安全 | 慢 | 不跨 | 强大 |
| QT | 高 | 小 | 安全 | 一般 | 跨 | 强大 |
| Tarui | 高 | 小 | 安全 | 慢 | 跨 | 小,不成熟 |
| Flutter | 暂无 | 暂无 | 暂无 | 暂无 | 跨 | 小 |
| NW | 一般 | 大 | 安全 | 快 | 跨 | 强大 |
| Electron | 一般 | 大 | 不安全 | 快 | 跨 | 非常强大 |

数据库 lowdb

electron 应用数据库有非常多的选择如 lowdb 、 sqlite3 、 electron-store 、 pouchdb 、 dedb 、 rxdb 、 dexie 、 ImmortalDB 等。这些数据库都有一个特性,那就是无服务器

electron 应用数据库技术选型考虑因素主要有以下3点:

生态(使用者数量、维护频率、版本稳定度)
能力
性能
其他(和使用者技术匹配度)

四个最优选择,分别是 lowdb 、 sqlite3 、 nedb 、 electron-store , 理由如下:

lowdb: 生态、能力、性能三方面表现优秀, json 形式的存储结构, 支持 lodash 、 ramda 等 api 操作,利于备份和调用
sqlite3: 生态、能力、性能三方面表现优秀, Nodejs 关系型数据库第一选择方案
nedb: 能力、性能三方面表现优秀,缺点是基本不维护了,但底子还在,尤其操作是 MongoDB 的子集,对于熟悉 MongoDB 的使用者来说是绝佳选择。
electron-store: 生态表现优秀,轻量级持久化方案,简单易用

数据库选型是 lowdb 方案。

PS:提一下 pouchdb ,如果需要将本地数据同步到远端数据库,可以使用 pouchdb ,其和 couchdb 可以轻松完成同步。

环境搭建

创建Electron跨平台应用之前,需要先安装一些常用的工具,如Node、vue和Electron等
node(v16.14.2,高了会报错)、vue都不说了,都知道怎么安装

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
53
54
55
56
57
58
59
60
//安装Electron

npm install -g electron

//验证是否安装成功

electron --version

//创建运行项目

//法一(cli2)(老旧,不推荐使用)
// 为什么不使用SimulatedGREG/electron-vue
// SimulatedGREG/electron-vue已经很久没有更新了,而且其生成的工程结构并不是vue-cli3。所以放弃使用

// 使用vue-cli脚手架工具来创建项目

mkdir my-electron-demo && cd my-electron-demo

vue init simulatedgreg/electron-vue

// 然后根据提示一步步选择自己所需的即可创建项目

// 再然后,使用npm install 安装依赖,安装完成之后,可以使用npm run dev或npm run build命令运行electron-vue模版应用程序


//当你npm install时候可能会报如下错误

// npm ERR! gyp info it worked if it ends with ok
// ...
// npm ERR! gyp ERR! cwd C:\...\node_modules\node-sass
// npm ERR! gyp ERR! node -v v16.14.0
// npm ERR! gyp ERR! node-gyp -v v3.8.0
// npm ERR! gyp ERR! not ok
// npm ERR! Build failed with error code: 1

//解决方案:安装最新版本的node-sass

npm install node-sass@latest

//法二(cli3 推荐)
vue create myproject

cd myproject

//通过运行以下命令安装并调用vue-cli-plugin-electron-builder的生成器
vue add electron-builder (如果看过我之前写的vue2和vue3共存的文章的话,这里要这么写:vue3 add electron-builder)

// 安装依赖
npm install

//启动
npm run electron:serve

//打包
npm run electron:build

//安装最新稳定版的 Electron
npm install --save-dev electron
//现有项目更新到最新的稳定版本
npm install --save-dev electron@latest

升级electron最新版(vue-cli安装后electron是13.0.0升级到最新版)

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
//执行如下 现有项目更新到最新的稳定版本
npm install --save-dev electron@latest

//①此时 npm run electron:serve 会报如下错

DeprecationWarning: Invalid 'main' field in 'C:\Users\BEIBEI\Desktop\my-electron-vue\dist_electron\package.json' of 'background.js'. Please either fix that or report it to the module author

解决方法 修改根目录配置文件vue.config.js

//修改前(只显示修改部分)
module.exports = defineConfig({
transpileDependencies: true
})

//修改后(只显示修改部分)
module.exports = defineConfig({
pluginOptions: {
electronBuilder: {
chainWebpackMainProcess: (config) => {
config.output.filename((file) => {
if (file.chunk.name === "index") {
return "background.js";
} else {
return "[name].js";
}
});
},
},
},
transpileDependencies: true
})

再次启动 npm run electron:serve 就可以了

//②还有可能报下面的错
Vue Devtools failed to install: Error: net::ERR_CONNECTION_TIMED_OUT
这是因为没有安装vue devtools

//网上有说安装 electron-devtools-installer
npm install electron-devtools-installer --save-dev (很久没更新了,不推荐)

vue3安装electron vue-tools要装beta版本,
详情参考下面这个地址
(https://blog.csdn.net/ashin8032/article/details/122600345)

打包

编辑根目录文件package.json,添加描述和作者

1
2
3
4
5
6
7
//编辑前(只显示部分内容)
"name": "my-electron-vue",

//编辑后(只显示部分内容)
"name": "my-electron-vue",
"description": "my electron+vue project",
"author": "author",

添加图标

桌面应用图标:
桌面应用图标大小256*256,且后缀为【.ico】。网页生成ico

在package.json中/vue.config.js中配置图标路径 (package.json中/vue.config.js中,这里配置的是控制打包后的桌面快捷方式的图标),如下,这是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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"build": {
"appId": "com.example.app", //包名
"productName":"aDemo",//项目名,也是生成的安装文件名,即aDemo.exe
"copyright": "Copyright © 2019",//版权信息
"directories":{ // 打包地址
"output":"./dist"//输出文件路径
},
"nsis": { // 安装过程的配置
"oneClick": false, // 是否一键安装
"allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序
"allowToChangeInstallationDirectory": true, // 允许修改安装目录
"installerIcon": "./XXX/logo.ico", // 安装图标
"uninstallerIcon": "./XXX/logo.ico", //卸载图标
"installerHeaderIcon": "./XXX/logo.ico", // 安装时头部图标
"createDesktopShortcut": true, // 创建桌面图标
"createStartMenuShortcut": true, // 创建开始菜单图标
"shortcutName": "idea", // 图标名称

"include": "build/script/installer.nsh", // 包含的自定义nsis脚本 这个对于构建需求严格得安装过程相当有用。
"script": "build/script/installer.nsh", // NSIS脚本的路径,用于自定义安装程序。 默认为build / installer.nsi。

//关于include 和 script 到底选择哪一个 ?
//在对个性化安装过程需求并不复杂,只是需要修改一下安装位置,卸载提示等等的简单操作建议使用include配置,如果你需要炫酷的安装过程,建议使用script进行完全自定义

},
"win": { // 图标路径
"icon": "public/icons/icon.ico",
"target": [
{
"target": "nsis",//利用nsis制作安装程序
"arch": [ // 这个意思是打出来32 bit + 64 bit的包,但是要注意:这样打包出来的安装包体积比较大,所以建议直接打32的安装包。
"x64",//64位
"ia32"//32位
]
}
]
},
"mac": {
"icon": "public/icons/icon.ico"
},
"linux": {
"icon": "public/icons/icon.ico"
},
},

vue3-Electron 窗口图标在测试时不变化,打包后改变,目前的现象是如此,配置可以写在vue.config.js中如下:
pluginOptions: {
electronBuilder: {
builderOptions: {
appId: "com.example.app", //包名
productName:"aDemo",//项目名,也是生成的安装文件名,即aDemo.exe
copyright: "Copyright © 2019",//版权信息
directories:{
output:"./dist"//输出文件路径
},
nsis: { // 安装过程的配置
oneClick: false, // 是否一键安装
allowToChangeInstallationDirectory: true, // 允许修改安装目录

installerIcon: "./public/icon.ico", // 安装图标
installerHeaderIcon: "./public/icon.ico" // 安装时头部图标
createDesktopShortcut: true, // 创建桌面图标
},
win: {
icon: './public/icon.ico' //打包windows版本的logo
},
"mac": {
"icon": "./public/icon.ico"
},
"linux": {
"icon": "./public/icon.ico"
},
productName: "vfirstss", //应用的名称
}
}
}

窗口图标
在主进程文件index.js(可能你的主进程文件叫main.js或者别的,我的叫background.js,总之领会精神就好,哈哈哈),这里配置的是窗口图标

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
mainWindow = new BrowserWindow({
...... // 因为这篇文章重点讲解图标配置,所以此处省略别的配置代码
height: 600,
width: 1020,
webPreferences: {
...
nodeIntegration: true
},
// 下面这行代码就是配置窗口图标的核心代码了
+ icon: path.join(__dirname, './XXX/logo20.ico'), // 注意,这里的path是一个node模块哦,需要npm安装并且引入使用。最直接的作用就是拼接字符串。
})

发现并没有变化?这个其实是正常的,BrowserWindow 对象的 icon 属性只对 windows/Linux 系统生效,对于 Mac OS 需要通过 app.dock.setIcon 进行设置

mainWindow = new BrowserWindow({
...... // 因为这篇文章重点讲解图标配置,所以此处省略别的配置代码
height: 600,
width: 1020,
webPreferences: {
...
nodeIntegration: true
},
// 下面这行代码就是配置窗口图标的核心代码了
++ icon: path.join(__dirname, 'assets/images/facetime.png'), //windows // 注意,这里的path是一个node模块哦,需要npm安装并且引入使用。最直接的作用就是拼接字符串。
})

++ if (process.platform === 'darwin') { //mac
++ app.dock.setIcon(path.join(__dirname, 'assets/images/facetime.png'));
++ }

设置标识

我们经常会发现图标右上方会有消息通知(Dock badges),比如 App Store 有多少个已安装的软件可以更新,QQ 上有多少条未读的消息等等。这个 Dock 标识在 Electron 中要如何设置呢?

我们可以通过 app.dock.setBadge API 进行设置。下面我们实现当应用窗口失去焦点时让消息通知的标识加1的功能

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
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
},
icon: path.join(__dirname, 'assets/images/facetime.png')
});

if (process.platform === 'darwin') {
app.dock.setIcon(path.join(__dirname, 'assets/images/facetime.png'));
}

mainWindow.loadFile('index.html');

mainWindow.on('close', function() {
mainWindow = null;
});

++ mainWindow.on('blur', () => {
++ const badgeString = app.dock.getBadge();
++ if (badgeString === '') {
++ app.dock.setBadge('1');
++ } else {
++ app.dock.setBadge((parseInt(badgeString) + 1).toString());
++ }
++ });
}

在项目中运行以下命令打包项目

1
npm run electron:build

等待打包完成
windows安装程序生成位置在 dist_electron\项目名 Setup 0.1.0.exe
windows免安装程序生成位置在 dist_electron\win-unpacked(绿色免安装包)\项目名

mac安装程序生成位置在 dist_electron\项目名-0.1.0.dmg

应用工程目录

使用electron-vue(vue2)模版创建的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
42
43
44
45
46
47
48
49
50
51
52
和前端工程的项目结构类似,Electron项目的目录结构如下所示:

electron-vue:Electron模版配置。

build:文件夹,用来存放项目构建脚本。

config:中存放项目的一些基本配置信息,最常用的就是端口转发。

node_modules:这个目录存放的是项目的所有依赖,即 npm install 命令下载下来的文件。

src:这个目录下存放项目的源码,即开发者写的代码放在这里。

static:用来存放静态资源。

index.html:则是项目的首页、入口页,也是整个项目唯一的HTML页面。

package.json:中定义了项目的所有依赖,包括开发时依赖和发布时依赖

对于开发者来说, 90% 的工作都是在 src 中完成,src 中的文件目录如上图所示

【主进程】
Electron 运行 package.json 的 main 脚本(background.js)的进程被称为主进程。在主进程中运行的脚本通过创建web页面来展示用户界面。一个 Electron 应用总是有且只有一个主进程。

【渲染进程】
由于 Electron 使用了 Chromium 来展示 Web 页面,所以 Chromium 的多进程架构也被使用到。每个 Electron 中的 Web 页面运行在它自己的渲染进程中。在普通的浏览器中,Web页面通常在一个沙盒环境中运行,不被允许去接触原生的资源。然而 Electron 允许用户在 Node.js 的 API 支持下可以在页面中和操作系统进行一些底层交互

【主进程与渲染进程通信】
主进程使用 BrowserWindow 实例创建页面。每个 BrowserWindow 实例都在自己的渲染进程里运行页面。当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止。主进程管理所有的Web页面和它们对应的渲染进程。每个渲染进程都是独立的,它只关心它所运行的 Web 页面

src目录结构
在Electron目录中,src会包包含main和renderer两个目录

main目录
main目录会包含index.js和index.dev.js两个文件。

index.js:应用程序的主文件,electron 也从这里启动的,它也被用作 webpack 产品构建的入口文件,所有的 main 进程工作都应该从这里开始。

index.dev.js:此文件专门用于开发阶段,因为它会安装 electron-debug 和 vue-devtools。一般不需要修改此文件,但它可以扩展开发的需求。
渲染进程
renderer是渲染进程目录,平时项目开发源码的存放目录,包含assets、components、router、

store、App.vue和main.js。

assets:assets下的文件如(js、css)都会在dist文件夹下面的项目目录分别合并到一个文件里面去。

components:此文件用于存放应用开发的组件,可以是自定义的组件。

router:如果你了解vue-router,那么Electron项目的路由的使用方式和vue-router的使用方式类似。

modules:electron-vue 利用 vuex 的模块结构创建多个数据存储,并保存在 src/renderer/store/modules 中。


打包并分发应用程序

官网说 最快捷的打包方式是使用 Electron Forge

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
// 将 Electron Forge 添加到您应用的开发依赖中,并使用其"import"命令设置 Forge 的脚手架:

npm install --save-dev @electron-forge/cli
npx electron-forge import

✔ Checking your system
✔ Initializing Git Repository
✔ Writing modified package.json file
✔ Installing dependencies
✔ Writing modified package.json file
✔ Fixing .gitignore

We have ATTEMPTED to convert your app to be in a format that electron-forge understands.

Thanks for using "electron-forge"!!!


// 使用 Forge 的 make 命令来创建可分发的应用程序

npm run make

> my-electron-app@1.0.0 make /my-electron-app
> electron-forge make

✔ Checking your system
✔ Resolving Forge Config
We need to package your application before we can make it
✔ Preparing to Package Application for arch: x64
✔ Preparing native dependencies
✔ Packaging Application
Making for the following targets: zip
✔ Making for target: zip - On platform: darwin - For arch: x64


//Electron-forge 会创建 out 文件夹,您的软件包将在那里找到

// Example for macOS
out/
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
├── ...
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app

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
42
43
44
45
46
47
Electron
├──atom - Electron 的源代码
| ├── app - 系统入口代码
| ├── browser - 包含了主窗口、UI 和其他所有与主进程有关的东西,它会告诉渲染进程如何管理页面
| | ├── lib - 主进程初始化代码中 JavaScript 部分的代码
| | ├── ui - 不同平台上 UI 部分的实现
| | | ├── cocoa - Cocoa 部分的源代码
| | | ├── gtk - GTK+ 部分的源代码
| | | └── win - Windows GUI 部分的源代码
| | ├── default_app - 在没有指定 app 的情况下 Electron 启动时默认显示的页面
| | ├── api - 主进程 API 的实现
| | | └── lib - API 实现中 Javascript 部分的代码
| | ├── net - 网络相关的代码
| | ├── mac - 与 Mac 有关的 Objective-C 代码
| | └── resources - 图标,平台相关的文件等
| ├── renderer - 运行在渲染进程中的代码
| | ├── lib - 渲染进程初始化代码中 JavaScript 部分的代码
| | └── api - 渲染进程 API 的实现
| | └── lib - API 实现中 Javascript 部分的代码
| └── common - 同时被主进程和渲染进程用到的代码,包括了一些用来将 node 的事件循环
| | 整合到 Chromium 的事件循环中时用到的工具函数和代码
| ├── lib - 同时被主进程和渲染进程使用到的 Javascript 初始化代码
| └── api - 同时被主进程和渲染进程使用到的 API 的实现以及 Electron 内置模块的基础设施
| └── lib - API 实现中 Javascript 部分的代码
├── chromium_src - 从 Chromium 项目中拷贝来的代码
├── docs - 英语版本的文档
├── docs-translations - 各种语言版本的文档翻译
├── spec - 自动化测试
├── atom.gyp - Electron 的构建规则
└── common.gypi - 为诸如 `node``breakpad` 等其他组件准备的编译设置和构建规则


平时开发时,需要重点关注的就是src、package.json和appveyor.yml目录。除此之外,其他需要注意的目录如下:

· script - 用于诸如构建、打包、测试等开发用途的脚本

· tools - 在 gyp 文件中用到的工具脚本,但与 script 目录不同, 该目录中的脚本不应该被用户直接调用

· vendor - 第三方依赖项的源代码,为了防止人们将它与 Chromium 源码中的同名目录相混淆, 在这里我们不使用 third_party 作为目录名

· node_modules - 在构建中用到的第三方 node 模块

· out - ninja 的临时输出目录

· dist - 由脚本 script/create-dist.py 创建的临时发布目录

· external_binaries - 下载的不支持通过 gyp 构建的预编译第三方框架

热更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 每次修改完代码,我们都需要重启,所以添加一个热更新依赖
yarn add --dev electron-reloader

//main.js修改如下:

const { app, BrowserWindow } = require('electron')

//热加载
const reLoader=require("electron-reloader")
reLoader(module)

//监听初始化完成的生命周期
app.on("ready",()=>{
const mainWindow = new BrowserWindow({
width: 700,
height: 700
})

mainWindow.loadFile('./src/index.html').then()
})

菜单的使用 参考文件

1

PS

APP窗口大小

修改background.js:

1
2
3
4
5
6
7
8
9
10
function createWindow () {
// Create the browser window.
win = new BrowserWindow({
+ width: 1200,
+ height: 620,
webPreferences: {
nodeIntegration: true
}
})
}

取消跨域限制

修改background.js:

1
2
3
4
5
6
7
8
9
10
11
function createWindow () {
// Create the browser window.
win = new BrowserWindow({
width: 1200,
height: 620,
webPreferences: {
+ webSecurity: false,
nodeIntegration: true
}
})
}

取消菜单栏

在我们生成的桌面APP中,我们可以看到默认的菜单栏。

在windows中,菜单栏在APP窗口内的顶部;在macOS中,菜单栏位于电脑屏幕顶部。

为了方便项目将来也能直接生成纯web应用,尽量把APP的全部功能都做到渲染进程里,这里我们取消菜单栏。

由于macOS的特殊性,顶部菜单栏无法删除,所以我们针对macOS特殊处理,把菜单栏只保留“关于”和“退出”。

修改background.js:

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
+   import { app, protocol, BrowserWindow, Menu } from 'electron'
...
function createWindow () {
...
win.on('closed', () => {
win = null
})
+ createMenu()
}

+ // 设置菜单栏
+ function createMenu() {
+ // darwin表示macOS,针对macOS的设置
+ if (process.platform === 'darwin') {
+ const template = [
+ {
+ label: 'App Demo',
+ submenu: [
+ {
+ role: 'about'
+ },
+ {
+ role: 'quit'
+ }]
+ }]
+ let menu = Menu.buildFromTemplate(template)
+ Menu.setApplicationMenu(menu)
+ } else {
+ // windows及linux系统
+ Menu.setApplicationMenu(null)
+ }
+ }

macOS菜单栏名称label的“App Demo”会在build版本生效,dev版本会显示“Electron”

Vue Devtools failed to install: Error: net::ERR_CONNECTION_TIMED_OUT

1

1

1

1

1

1

1

------ The End ------
您的认可是我不断进步的动力!