electron 入门
什么是 electron?
Electron 可以让你使用纯 JavaScript 调用丰富的原生 APIs 来创造桌面应用。你可以把它看作一个专注于桌面应用的 Node.js 的变体,而不是 Web 服务器。
这不意味着 Electron 是绑定了 GUI 库的 JavaScript。相反,Electron 使用 web 页面作为它的 GUI,所以你能把它看作成一个被 JavaScript 控制的,精简版的 Chromium 浏览器。
electron Hello World
1
2
3
| mkdir tasky
cd tasky
npm init
|
1
| cnpm install electron --S # 保存到dependency
|
- 根目录创建 index.js,Node.js 项目的入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // 引入两个模块:app 和 BrowserWindow
// app 模块,控制整个应用程序的事件生命周期。
// BrowserWindow 模块,它创建和管理程序的窗口。
const { app, BrowserWindow } = require('electron')
// 在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口
app.on('ready', () => {
// 创建一个窗口
const mainWindow = new BrowserWindow()
//窗口加载html文件
mainWindow.loadFile('./src/main.html')
})
|
- 创建窗口需要加载的 html 文件:main.html
新建一个 src
文件夹,用于存放 web 页面资源,比如 html、css、js、图片等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // ./src/main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
hello world
</body>
</html>
|
- 修改 package.json 的 scripts,如下:
1
2
3
| "scripts": {
"start": "electron ."
}
|
热更新 nodemon
nodemon 热更新
“start” : “electron .” 改成下面
1
2
3
4
| "scripts": {
"start": "nodemon --watch index.js --exec electron ."
},
|
- 再次运行
npm run start
,当 index.js 内容变化时,就会自动重新执行 electron .
来重启应用。
调试
加 log
1
| console.log('app on ready.。。');
|
窗口页面的调试方法和 chrome 浏览器类似。点击菜单栏的 View --- Toggle Developer Tools
,或者按它对应的快捷键,就会出现我们熟悉的开发者工具界面。
当页面内容发生变化,可以点击 View --- Reload
,或者快捷键 ctrl+r
,刷新页面内容。
electron API
BrowserWindow 窗口
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
| // Import parts of electron to use
// app 控制应用生命周期的模块
// BrowserWindow用于创建原生窗口
// Tray用于创建托盘图标
// Menu菜单模块
// ipcMain 用于主进程和渲染进程通信
const { app, BrowserWindow, Tray, Menu, ipcMain } = require('electron');
// 创建一个窗口
let mainWindow = new BrowserWindow({
frame: false, // 隐藏窗口边框和标题栏
resizable: false, // 禁止改变窗口大小
width: 800,
height: 600,
icon: iconPath, // 应用运行时的标题栏图标
webPreferences: {
nodeIntegration: true, // 在渲染进程中使用nodejs的API,IPC的时候用
backgroundThrottling: false, // 设置应用在后台可以运行:解决页面被挂起后定时器无法正常工作的问题
contextIsolation: false, // 设置是否在渲染进程中启用上下文隔离
}
});
mainWindow.removeMenu(); // 隐藏菜单栏,防止通过快捷键打开菜单栏,比如:ctrl+shift+i开发者工具
// 加载main.html
mainWindow.loadFile('src/main.html');
|
无边框窗口
- 要创建无边框窗口,在 BrowserWindow 的 options 中将
frame
设置为 false
1
2
3
4
| mainWindow = new BrowserWindow({
frame: false,
// ...
})
|
- 默认情况下,无边框窗口是不可拖拽的。应用程序需要在 CSS 中指定
-webkit-app-region: drag
来告诉 Electron 哪些区域是可拖拽的。
1
2
3
4
5
6
7
8
| html,body {
height: 100%;
width: 100%;
}
body{
-webkit-app-region: drag;
}
|
如果用上面的属性使整个窗口都可拖拽,则必须将其中的按钮标记为不可拖拽,否则按钮将无法点击。
1
2
3
| .enable-click {
-webkit-app-region: no-drag;
}
|
Tray 托盘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // Import parts of electron to use
// app 控制应用生命周期的模块
// BrowserWindow用于创建原生窗口
// Tray用于创建托盘图标
// Menu菜单模块
// ipcMain 用于主进程和渲染进程通信
const { app, BrowserWindow, Tray, Menu, ipcMain } = require('electron');
tray = new Tray(iconPath) // 实例化一个tray对象,构造函数的唯一参数是需要在托盘中显示的图标url
tray.setToolTip('tasky') // 设置鼠标悬浮在托盘图标上时的提示文本
tray.on('click', () => { // 给托盘图标绑定单击事件
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
});
tray.on('right-click', () => { // 给托盘图标绑定右键单击事件
const contextMenu = Menu.buildFromTemplate([ // 创建一个菜单模板
{
label: '退出', click: () => {
app.quit()
}
}
])
tray.popUpContextMenu(contextMenu) // 给托盘图标绑定菜单
});
|
ipcRenderer IPC 通信
IPC(Inter-Process Communication),就是进程间通信。Electron 应用程序区分主进程和渲染进程,有时候,两者之间需要通信,传输一些数据、发送一些消息。
渲染进程 👉 主进程
渲染进程使用 Electron 内置的 ipcRenderer
模块向主进程发送消息,ipcRenderer.send
方法的第一个参数是消息管道名称:
1
2
3
4
5
6
7
| // 页面的js代码:
const electron = require('electron')
const { ipcRenderer } = electron
closeDom.addEventListener('click', () => {
ipcRenderer.send('mainWindow:close')
})
|
主进程通过 ipcMain
接收消息,ipcMain.on
方法的第一个参数也为消息管道的名称,与 ipcRenderer.send
的名称对应,第二个参数是接收到消息的回调函数:
1
2
3
4
| // 入口文件index.js
ipcMain.on('mainWindow:close', () => {
mainWindow.hide()
})
|
主进程 👉 渲染进程
主进程向渲染进程发送消息是通过渲染进程的 webContents
。在 mainWindow 渲染进程设定了任务后,会传输给主进程任务信息,当任务时间到了,主进程会创建提醒窗口 remindWindow,并通过 remindWindow.webContents
将任务名称发给 remindWindow。
1
2
3
4
5
6
7
8
9
10
| function createRemindWindow (task) {
remindWindow = new BrowserWindow({
//options
})
remindWindow.loadURL(`file://${__dirname}/src/remind.html`)
// 主进程发送消息给渲染进程
remindWindow.webContents.send('setTask', task)
}
|
在 remindWindow 渲染进程中,通过 ipcRenderer.on 接受消息:
1
2
3
4
| ipcRenderer.on('setTask', (event,task) => {
document.querySelector('.reminder').innerHTML =
`<span>${decodeURIComponent(task)}</span>的时间到啦!`
})
|
渲染进程 👉 渲染进程
渲染进程之间传递消息,可以通过主进程中转,即窗口 A 先把消息发送给主进程,主进程再把这个消息发送给窗口 B,这种非常常见。
也可以从窗口 A 直接发消息给窗口 B,前提是窗口 A 知道窗口 B 的 webContents 的 id。
1
| ipcRenderer.sendTo(webContentsId, channel, ...args)
|
值得注意的是,我们在页面的 js 代码中使用了 require,这也是 Electron 的一大特点,在渲染进程中可以访问 Node.js API。这样做的前提是在创建窗口时配置 webPreferences
的 nodeIntegration: true
和 contextIsolation: false
:
1
2
3
4
5
6
7
8
| mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences:{
nodeIntegration: true,
contextIsolation: false
}
})
|
Ref