使用 Electron 构建桌面应用
Electron 是一个基于 Node.js,并拥有一个 Chromium「外壳」的桌面应用开发框架。你可以调用所有 Node.js 和浏览器的 API,使用 JavaScript,HTML 和 CSS 等 Web 技术创建原生程序。根据官方的宣传语:「它负责比较难搞的部分,你只需把精力放在你的应用的核心上即可。」
下面介绍如何使用 Electron 构建简单的桌面应用。你可以参照官方给出的入门程序:1
git clone https://github.com/electron/electron-quick-start.git
安装
Electron 可以通过 npm install electron
来进行安装。在依赖包安装完成后,Electron 会开始下载它的「本体」—— 一个数十 MiB 大小的压缩包,包含 Electron 在不同平台下的可执行文件。这一步对于国内的开发者不太友好,因为下载的内容在 Amazon 云上,访问速度不佳;而且新版的 Electron 去除了下载进度条,导致即使出现问题,你也无法知晓卡在了哪一步。一种简单的解决方案是通过设定环境变量来使用淘宝源:1
ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
设置此环境变量后,再执行 npm install
,下载就是正常的了。
开始构建
一个 Electron 应用应包含以下内容:
main.js
当你启动 Electron 程序时,这是最先被执行的脚本。在 main.js
中可以创建窗口对象以构建图形界面。一个典型的 main.js
如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24const { app, BrowserWindow } = require("electron");
function createWindow() {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
});
mainWindow.loadFile("index.html");
}
app.whenReady().then(() => {
createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});
mainWindow
即为 BrowserWindow
窗口对象。mainWindow
有很多参数可以设置,例如,可以将 width
和 height
替换为1
2width: electron.screen.getPrimaryDisplay().workAreaSize.width,
height: electron.screen.getPrimaryDisplay().workAreaSize.height
来实现全屏。在创建了窗口对象后,可以使用1
mainWindow.setAlwaysOnTop(true, "");
来使应用置顶(在 macOS 下,第二个参数将决定置顶的级别,例如,第二个参数取 "floating"
会使窗口置于大部分应用之前,但会被 KeyNote 等级别更高的应用挡住;而取 "main-menu"
则即使在演示 KeyNote 该窗口也能置于顶层)。 如果窗口设置了全屏置顶,就需要配合以下代码使用:1
mainWindow.setIgnoreMouseEvents(true);
这样可以使窗口忽略鼠标事件,否则就不能正常操作其他窗口了。process.platform !== "darwin"
的作用是,在 macOS 上,关闭所有窗口并不意味着退出程序 —— 你可以点击 Dock 中的程序图标唤醒它(即触发 activate
事件);而在 Windows 中,关闭所有窗口的默认行为是直接退出程序。
index.html
这是窗口的 HTML 文件。在 main.js
中,使用了 mainWindow.loadFile()
方法来加载页面内容。loadFile
中 index.html
也可以替换为任何自定义页面。
在创建窗口后,index.html
会在 Electron 创建的 Chromium 环境下加载,接下来的工作就是进行前端设计,来构建桌面应用的页面。
在这个 index.html
中,你可以使用 <script>
标签来加载 JavaScript 代码,就像平常的 HTML 页面一样。
前面 main.js
中有这样一串代码:1
2
3webPreferences: {
nodeIntegration: true
}
在设定 nodeIntegration: true
之后,这里的 JS 不仅可以访问 DOM,还能使用 Node.js 所有的 API。能前能后,想怎么玩都行。你甚至可以使用 jQuery,react.js
或 vue.js
等框架。不过需要注意的是,如果使用 jQuery,最好不要像这样加载1
<script src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script>
此时由于 module
的冲突,jQuery 并没有被定义为全局变量,也无法通过 window.$
被访问和使用。
推荐的做法是通过 npm
安装 jQuery:1
npm install jquery
然后在页面中用 CommonJS 模块的方式加载1
window.jQuery = window.$ = require("jquery");
如果你需要更多的页面,可以创建 HTML 文件并使用 new BrowserWindow()
加载这些页面。
dialog.showMessage()
方法
还要注意的是,alert()
会导致渲染进程阻塞(在全屏状态下使用会有严重问题),应该使用1
2
3
4const { dialog } = require("electron").remote;
dialog.showMessageBox({
message: msg
});
像这样调用 dialog.showMessage()
方法显示消息,msg
参数就是消息的内容。这是异步的,对应的同步方法是 dialog.showMessageBoxSync()
。它和 alert()
一样会阻止其它代码的执行。
相关内容可以查看:
https://github.com/electron/electron/pull/17298
https://github.com/electron/electron/issues/20855
页面间通讯
此外,Electron 提供了在不同页面间进行通讯的 API,在父页面使用1
mainWindow.webContents.send(channel, messageContent);
发送消息,其中 channel 是任意字符串,表示通信频道,messageContent 是消息内容。在子页面使用1
2
3
4const { ipcRenderer } = require("electron");
ipcRenderer.on(channel, (event, message) => {
console.log(message);
});
监听消息即可。
禁止拖拽文件
在旧版的 Electron 中,将一个 HTML 文件拖拽进页面中,会导致页面跳转。这一行为有些不太友好(因为 Electron 没有浏览器的返回键),需要通过设置 addEventListener
来禁用掉这一行为。不过这也被官方注意到,并且修复了:
https://github.com/electron/electron/pull/12655
现在应该不会遇到由于拖拽造成的问题了。
打包
在设计完成后,使用命令 npx electron .
即可运行。如果是全局安装的 electron
,则执行 electron .
。
如果需要将应用打包,可以使用 electron-builder
(用 npm
安装),在 Windows 系统下就会默认打包成.exe
格式,macOS 则为.dmg
。当然,在 macOS 上打包 Windows 版本的可执行文件也不是问题,具体可以看文档。macOS 10.15 出现了一个小 bug,不过影响不大,将 electron-builder
升级到最新就能解决:
https://github.com/electron-userland/electron-builder/issues/4305#issuecomment-559138959
发挥你的想象力,便能使用 Electron 构建功能丰富的跨平台应用啦!
参考文章:
使用 electron 构建跨平台 Node.js 桌面应用经验分享
Electron API 演示
官方文档:Electron 文档