Electron-vue开发实战(二)| Main进程开发
认识目录⁍
使用vue-cli创建的electron工程文件目录大体结构[1]如下:
1 |
|
electron有多种文件结构,例如将所有应用的代码都放在app目录中,资源文件位于static目录
我们重点关注的是主进程、渲染进程和配置文件,其中开发主要集中在渲染进程中
package.json[^3]⁍
许多Node项目中都会有package.json清单文件,它是electron项目的依赖文件,核心参数之一是应用的主入口。
对于纯electron应用:
1 |
|
dist目录全称是distribution。在某些框架中,因为开发和发布是的内容或者代码形式是不一样的(比如利用Grunt压缩等等),这时候就需要一个存放最终发布版本的代码,这就是dist文件夹的用处。[2]
上面的代码根据3中对electron应用的讲解,electron-vue框架搭建出来,这里显示的有些不一样,可能涉及到发布/开发模式的问题?不知道以后编译啊之类的会不会就有区别;看PicGo的项目这里差别也很大
主进程 src/main
⁍
在3的纯electron应用中,它将主进程写入main.js
文件中,作为项目的入口;而在electron-vue框架中,主进程在src/main/index.js
中。代码的整体结构是相似的。
在main进程中主要需要掌握以下知识
app模块⁍
app
可以处理应用的生命周期与配置,是electron应用的骨架。它掌管着整个应用的生命周期钩子,以及很多其他事件钩子。
引用模块
1 |
|
-
app的常用生命周期钩子如下:
-
will-finish-launching
在应用完成基本启动进程之后触发 -
ready
当electron完成初始化后触发(必须)1
2
3
4
5
6
7
8function createWindow () { // 创建窗口
/**
* Initial window options
*/
// ...
}
app.on('ready', createWindow) // 调用createWindow创建窗口 -
window-all-closed
所有窗口都关闭的时候触发,在windows和linux里,所有窗口都退出的时候通常是应用退出的时候1
2
3
4
5app.on('window-all-closed', () => {
if (process.platform !== 'darwin') { // 当操作系统不是darwin(macOS)的话
app.quit() // 退出应用
}
}) -
before-quit
退出应用之前的时候触发 -
will-quit
即将退出应用的时候触发 -
quit
应用退出的时候触发(用法见window-all-closed
)
-
-
事件钩子
-
active
(仅macOS)当应用处于激活状态时1
2
3
4
5app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
}) -
browser-window-created
当一个BrowserWindow被创建的时候 -
browser-window-focus
当一个BrowserWindow处于激活状态的时候
这些钩子需要配合一些具体场景来做出具体的操作。比如当一个BrowserWindow处于激活状态的时候修改窗口的title值。
-
-
其他常用的方法:
-
app.quit()
用于退出应用 -
app.getPath(name)
用于获取一些系统目录,对于存放应用的配置文件等很有用 -
app.focus()
用于激活应用,不同系统激活逻辑不一样
-
而我们通常会在ready
的时候执行创建应用窗口、创建应用菜单、创建应用快捷键等初始化操作。而在will-quit
或者quit
的时候执行一些清空操作,比如解绑应用快捷键。
BrowserWindow渲染器进程⁍
主进程可以使用BrowserWindow
模块创建多个独立的、互相隔离的渲染器进程(即应用窗口)。
1 |
|
创建BrowserWindow
实例:
1 |
|
常见配置和方法⁍
可以通过添加参数来改变配置
一个BrowserWindow的常用配置:
1 |
|
PicGo主窗口写法参考:
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
const createSettingWindow = () => {
const options = {
height: 450,
width: 800,
show: false,
frame: true,
center: true,
fullscreenable: false,
resizable: false,
title: 'PicGo',
vibrancy: 'ultra-dark',
transparent: true,
titleBarStyle: 'hidden',
webPreferences: {
backgroundThrottling: false
}
}
if (process.platform === 'win32') { // 针对windows平台做出不同的配置
options.show = true // 创建即展示
options.frame = false // 创建一个frameless窗口
options.backgroundColor = '#3f3c37' // 背景色
}
settingWindow = new BrowserWindow(options)
settingWindow.loadURL(settingWinURL)
settingWindow.on('closed', () => {
settingWindow = null
})
}在PicGo项目中,window的配置被保存在
main/apis/app/window/windowList.ts
中,然后export
出去,在windowManager
里对窗口进行统一管理
跟app
模块一样,BrowserWindow
也有很多常用的事件钩子:
closed
当窗口被关闭的时候focus
当窗口被激活的时候show
当窗口展示的时候hide
当窗口被隐藏的时候maxmize
当窗口最大化时minimize
当窗口最小化时...
当然,也依然有很多实用的方法:
BrowserWindow.getFocusedWindow()
[静态方法]获取激活的窗口win.close()
[实例方法,下同]关闭窗口win.focus()
激活窗口win.show()
显示窗口win.hide()
隐藏窗口win.maximize()
最大化窗口win.minimize()
最小化窗口win.restore()
从最小化窗口恢复...
比如上述说到的,windows的顶部的操作区(放大、缩小、关闭按钮)就可以通过icon模拟+实例方法来实现
将html文档加载到主窗口⁍
方法一:
1 |
|
方法二:(electron-vue框架自带)
1 |
|
__dirname
变量是Node全局可用的一个变量,它的值是当前正被执行的Node应用的完整路径。
win.webContents
此窗口拥有的 WebContents
对象。 所有与网页相关的事件和操作都将通过它完成。
-
win.loadURL(url[, options])
与webContents.loadURL(url[, options\])
相同。 -
win.loadFile(filePath[, options])
与webContents.loadFile
相同,filePath
应该是一个与你的应用程序的根路径相关的HTML文件路径。
两者在用法上有一定差别,可以参考BrowserWindow | Electron (electronjs.org)
优雅地显示窗口⁍
在加载页面时,渲染进程第一次完成绘制时,如果窗口还没有被显示,渲染进程会发出 ready-to-show
事件 。 在此事件后显示窗口将没有视觉闪烁
1 |
|
Tray系统托盘⁍
添加图标和上下文菜单到系统通知区。在windows里,Tray
配合上图标之后就是windows右下角的应用图标
windows和macOS里,图标的大小都是16*16
px。
官网代码(配合Menu使用):
1 |
|
- 在 Windows 上, 建议使用
ICO
图标来获得最佳视觉效果。
如果要在所有平台上保持完全相同的行为, 则不应依赖 click
事件, 并且始终将上下文菜单附加到任务栏图标。
macOS下顶部栏的图标通常都是走黑白
路线,所以可以为两种系统分别准备不同的图标。PicGo
里Tray
的生成代码大致如下:
1 |
|
注意上述代码里有一个${__static}
的变量。该变量是electron-vue
为我们暴露出来的项目根目录下的static
文件夹的路径。通过这个路径,在开发和生产阶段都能很好的定位你的静态资源所在的目录。是个很方便的变量。
Tray支持很多有用的事件。其中最关键的两个是click
和right-click
。分别对应鼠标左键点击和鼠标右键点击事件。
鼠标左键点击事件⁍
- 在macOS系统下,鼠标左键点击Tray的icon可能会出现配置菜单,也有可能会出现应用窗口。
- 在windows下,鼠标左键点击Tray的icon通常会出现应用的窗口。
鼠标右键点击事件⁍
- 在macOS系统下,鼠标右键点击Tray的icon通常会出现配置菜单。
- 在windows系统下,同上。
PicGo举例:
1 |
|
Menu⁍
new Menu()
创建新菜单。
主要分两种。
-
第一种是app的菜单。对于macOS来说就是顶部栏左侧区域的菜单项。对于windows而言就是一个窗口的标题栏下方的菜单区。
可以通过
Menu.setApplicationMenu()
来实现。 -
第二种是类似于右键菜单的菜单。
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
56const contextMenu = Menu.buildFromTemplate([
{
label: '关于',
click () {
dialog.showMessageBox({
title: 'PicGo',
message: 'PicGo',
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
})
}
},
{
label: '打开详细窗口',
click () {
if (settingWindow === null) {
createSettingWindow()
settingWindow.show()
} else {
settingWindow.show()
settingWindow.focus()
}
}
},
{
label: '选择默认图床',
type: 'submenu',
submenu: [
{
label: '微博图床',
type: 'radio',
checked: db.read().get('picBed.current').value() === 'weibo',
click () {
db.read().set('picBed.current', 'weibo')
.write()
}
},
{
label: '七牛图床',
type: 'radio',
checked: db.read().get('picBed.current').value() === 'qiniu',
click () {
db.read().set('picBed.current', 'qiniu')
.write()
}
}
]
},
{
role: 'quit',
label: '退出'
}
])
tray.on('right-click', () => {
tray.popUpContextMenu(contextMenu)
})
组成Menu
的是一个一个的MenuItem
。它们有很多类型:
- normal
- separator
- submenu
- checkbox
- radio
以及很多角色:
- quit
- copy
- redo
- undo
- minimize
- close
- reload
- …
通常来说,配置的菜单项基本从类型里来组合。
如果没有在创建app菜单里指定这些操作的快捷键的话,那么一些常见的快捷操作就无法在你的app里使用了。比如ctrl+c
或者command+c
复制这个操作,如果你没有通过Menu.setApplicationMenu()
来设定这个快捷键的话,那么在你的electron应用里就无法执行复制的操作了。
注意,如果在开发模式下直接只使用如下快捷键的话,一些调试快捷键比如
F12
或者command+shift+i
打开控制台的操作就无法使用了。所以在开发模式下不需要创建这些快捷键菜单。
1 |
|
可以通过accelerator
指定你想要的快捷键。诸如Shift
、Ctrl
、Cmd
等键位缩写。如果是组合键,就加上+
。尤其注意到,因为macOS和windows键位的差异,所以有一个很好用的键位缩写CmdOrCtrl
,即如果是在macOS上就是Cmd
,在windows上就是Ctrl
。
菜单项的点击事件可以直接通过click
属性来指定。先通过了Menu.buildFromTemplate()
这个方法创建了菜单,然后再在右键点击Tray
图标的时候将其弹(PopUp)出来。
当然也有其他构建菜单的方法。可以通过Menu实例的append
方法来加入Menu Item
。
总结⁍
本文主要还是结合了PicGo项目对Electron主进程有了大致的了解
PicGo项目本身对功能的划分比较明确,它的入口是backgroung.js
,通过它来调用main/lifeCycle/index.js
,在其中来进行ready/quit等,并调用写入其他文件里的窗口创建/菜单创建等。