Electron入门实践总结

2.png

最近,用Electron写了一个桌面应用,是一个比较简单的应用,所以用的东西都是最浅的,做一个总结吧。

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

安装

因为是基于nodejs,所以先要安装nodejs,这就不多说了

创建一个项目文件夹,执行

1
npm init

之后在安装electron
这里用的淘宝的镜像,所以是

1
cnpm install -g electron

大体上,一个 Electron 应用的目录结构如下:

1
2
3
4
your-app/
├── package.json
├── main.js
└── index.html

package.json的格式和 Node 的完全一致,并且那个被 main 字段声明的脚本文件是你的应用的启动脚本,它运行在主进程上。你应用里的 package.json 看起来应该像:

1
2
3
4
5
6
7
8
9
{
"name" : "your-app",
"version" : "0.1.0",
"main" : "main.js" //这里要与目录的里的文件名保持一致
"scripts": {
"start": "electron .",
"test": "echo \"Error: no test specified\" && exit 1"
}
}

注意:如果 main 字段没有在 package.json 声明,Electron会优先加载 index.js

快速搭建

clone github上的electron-quick-start项目
按照以下流程便可以快速启动了

1
2
3
4
5
6
7
git clone https://github.com/electron/electron-quick-start
# Go into the repository
cd electron-quick-start
# Install dependencies
npm install
# Run the app
npm start

实现热更新

如果你不改变菜单以及main.js的情况下,你更新了页面的结构和样式,可以使用==Ctrl+R==来刷新页面,但是修改了main.js,你可能需要频频终止,然后再重新启动,有什么方式可以实现这些文件一发生变化就更新呢?
这里使用的是用gulp创建一个任务。

1
2
3
cnpm install gulp -g //全局安装
cnpm install --save-dev gulp
cnpm install electron-connect

如果安装中间报错了,说缺失什么模块,
那就先cnpm install 该模块,在重新安装即可。

然后在项目目录下新建==gulpfile.js==

1
2
3
4
5
6
7
8
9
var gulp = require('gulp');
var gutil = require('gulp-util');
var electron = require('electron-connect').server.create();
gulp.task('watch:electron', function () { // watch:electron 为你的任务名字
electron.start();
gulp.watch(['main.js'], electron.restart);//这里的路径根据你的文件路径来
gulp.watch(['*.html','assets/**/*.{html,js,css}'], electron.reload);//这里的路径根据你的文件路径来
});

在主进程main.js里添加

1
var client = require('electron-connect').client;

在每一个渲染进程xx.html页面里都添加上

1
require('electron-connect').client.create();

之后直接通过以下来启动即可。

1
gulp watch:electron

因为是基于nodejs,还有其他多种热更新的方式,这里不多说啦。

主进程和渲染进程通信

主进程就是main.js运行的进程
渲染进程即是渲染一个个客户端的前端页面中。
主进程和渲染进程之间通信通过Electron ipcRenderer模块,提供了方法,从渲染进程向主进程发送同步或异步消息. 也可以收到主进程的相应的消息。

消息监听

ipcRenderer 模块有下列方法来监听事件:

1
2
3
ipcRenderer.on(channel, listener)
channel String
listener Function

监听 channel, 当有新消息到达,使用 listener(event, args…) 调用 listener .

1
2
3
ipcRenderer.once(channel, listener)
channel String
listener Function

为这个事件添加一个一次性 listener 函数.这个 listener 将在下一次有新消息被发送到 channel 的时候被请求调用,之后就被删除了.

发送消息

1
2
3
4
5
ipcRenderer 模块有如下方法来发送消息:
ipcRenderer.send(channel[, arg1][, arg2][, ...])
channel String
arg (可选)

通过 channel 向主进程发送异步消息,也可以发送任意参数.参数会被JSON序列化,之后就不会包含函数或原型链.

主进程通过使用==ipcMain== 模块来监听 channel,从而处理消息.

1
2
3
ipcRenderer.sendSync(channel[, arg1][, arg2][, ...])
channel String
arg (可选)

注意: 发送同步消息将会阻塞整个渲染进程,除非你知道你在做什么,否则就永远不要用它 .

栗子

在main.js中

1
2
3
4
5
6
const ipcMain = require('electron').ipcMain;
ipcMain.on('getData-message', function (event, arg) { //主程序监听名为getData-message事件
console.log(arg);//“success” ,arg为渲染进行发送的数据
event.sender.send('getData-reply', "message received"); //向渲染进程“getData-reply”事件发送数据
});

在渲染进程index.html中

1
2
3
4
5
const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.send('getData-message',“success”);//向主进程“getData-message”事件发送数据
ipcRenderer.on('getData-reply', function (event, arg) { //监听名为getData-reply事件
console.log(arg);//"message received"
})

BrowserWindow的一些设置

1
new BrowserWindow([options])
  • resizable Boolean - 是否可以改变窗口size,默认为 true。
  • frame Boolean - 指定 false 来创建一个 无边框 Window. 默认为 true。
  • autoHideMenuBar Boolean - 除非点击 Alt,否则隐藏菜单栏.默认为 false

设置菜单

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
var template = [
{
label: 'Edit',
submenu: [
{
label: 'Undo',
accelerator: 'CmdOrCtrl+Z',
role: 'undo'
},
{
label: 'Redo',
accelerator: 'Shift+CmdOrCtrl+Z',
role: 'redo'
},
{
type: 'separator'
},
{
label: 'Cut',
accelerator: 'CmdOrCtrl+X',
role: 'cut'
},
{
label: 'Copy',
accelerator: 'CmdOrCtrl+C',
role: 'copy'
},
{
label: 'Paste',
accelerator: 'CmdOrCtrl+V',
role: 'paste'
},
{
label: 'Select All',
accelerator: 'CmdOrCtrl+A',
role: 'selectall'
},
]
},
{
label: 'View',
submenu: [
{
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
click: function(item, focusedWindow) {
if (focusedWindow)
focusedWindow.reload();
}
},
{
label: 'Toggle Full Screen',
accelerator: (function() {
if (process.platform == 'darwin')
return 'Ctrl+Command+F';
else
return 'F11';
})(),
click: function(item, focusedWindow) {
if (focusedWindow)
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
}
},
{
label: 'Toggle Developer Tools',
accelerator: (function() {
if (process.platform == 'darwin')
return 'Alt+Command+I';
else
return 'Ctrl+Shift+I';
})(),
click: function(item, focusedWindow) {
if (focusedWindow)
focusedWindow.toggleDevTools();
}
},
]
},
{
label: 'Window',
role: 'window',
submenu: [
{
label: 'Minimize',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
},
{
label: 'Close',
accelerator: 'CmdOrCtrl+W',
role: 'close'
},
]
},
{
label: 'Help',
role: 'help',
submenu: [
{
label: 'Learn More',
click: function() { require('electron').shell.openExternal('http://electron.atom.io') }
},
]
},
];
if (process.platform == 'darwin') {
var name = require('electron').remote.app.getName();
template.unshift({
label: name,
submenu: [
{
label: 'About ' + name,
role: 'about'
},
{
type: 'separator'
},
{
label: 'Show All',
role: 'unhide'
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Command+Q',
click: function() { app.quit(); }
},
]
});
// Window menu.
template[3].submenu.push(
{
type: 'separator'
},
{
label: 'Bring All to Front',
role: 'front'
}
);
}
var menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

设置任务栏

想要在电脑右下方有设置任务栏及图标,需要用到Tray模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const electron = require('electron');
const app = electron.app;
const Menu = electron.Menu;
const Tray = electron.Tray;
var appIcon = null;
app.on('ready', function(){
appIcon = new Tray('/path/to/my/icon');
var contextMenu = Menu.buildFromTemplate([
{ label: 'Item1', type: 'radio',click:function(){ console.log("任务栏点击事件")}},
{ label: 'Item2', type: 'radio' },
{ label: 'Item3', type: 'radio', checked: true },
{ label: 'Item4', type: 'radio' }
]);
appIcon.setToolTip('This is my application.');
appIcon.setContextMenu(contextMenu);
});

参考
https://www.w3cschool.cn/electronmanual/