一篇文章学会Electron
【前言】
javascript语言的强大生命力,为前端开发拓展了很多业务
网页端业务:基于js,html,css开发网页相关业务
服务端业务:基于nodejs开发服务端业务
桌面端业务:基于chromium和nodejs开发桌面业务
移动端业务:基于webview开发移动端业务
【electron-简介】
桌面端业务,也就是mac和windows上的应用开发业务,
是基于chromium和nodejs来开发的,
chromium
地址:https://www.chromium.org/chromium-projects/
chromium负责渲染进程(ui层),chromium也就是chrome内核,
由于chromium基本支持js,html,css最新的特性,
所以渲染层的各种实现不在话下
nodejs
地址: https://nodejs.org/dist/latest-v16.x/docs/api/
nodejs大家比较熟悉,跨平台的runtime,具备文件,网络等能力,
例如将mac,windows和linux下的文件能力都封装为fs模块,
所以nodejs负责主进程,抹平各操作系统的差异,
electron
地址: https://www.electronjs.org/
electron大概做了几件事情
1.内嵌了chromium和nodejs,具备了渲染进程和主进程能力
2.提供ipc通信,打通渲染进程和主进程之间通信的能力
3.实现各种操作系统的特性,例如mac下图标,任务栏等特性
electron的性能
地址: https://www.electronjs.org/apps
跨平台的技术一般会被挑战性能方面的指标,
上述地址是用electron开发的应用列表,
可以看到有很多大型应用都使用electron开发,
例如:vscode,figma,postman,slack,discord等等,
覆盖面很广,从工具类vscode,postman到社交应用slack和discord都有,
侧面证明了electron性能是ok的,
如果你开发出的electron应用性能差,要更多的从自己业务代码上找原因

【electron-hello world】
electron官方提供了入门的示例,跑起一个hello world,
首先需要安装electron
electron:安装
electron安装比较简单,命令如下,
-D是因为electron这个包在开发时提供环境,
构建为mac,windows应用时会通过一些打包工具将electron的能力打包进去,
npm i -D electron
这里需要注意的是,第一次安装时要下载electron,
这个包比较大,建议选一个网络比较好的环境下载
安装后可以参考官网示例,示例中大概有这几个文件:
index.html
对应上面说的渲染进程,渲染层,ui层,运行在chromium上,
其实就是一个html,可以看到内容很简单,甚至没有引用css
<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
We are using Node.js <span id="node-version"></span>, Chromium <span id="chrome-version"></span>, and Electron
<span id="electron-version"></span>. <!-- 您也可以此进程中运行其他文件 -->
<script src="./renderer.js"></script>
</body>
</html>
main.js
对应上面说的主进程,运行在nodejs环境上,
逻辑也比较简单,创建了一个window,
为这个window加载了preload.js文件
监听window的一些事件
// main.js
// 控制应用生命周期和创建原生浏览器窗口的模组
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
// 创建浏览器窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
// 加载 index.html
mainWindow.loadFile('index.html');
// 打开开发工具
// mainWindow.webContents.openDevTools()
}
// 这段程序将会在 Electron 结束初始化
// 和创建浏览器窗口的时候调用
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(() => {
createWindow();
app.on('activate', function () {
// 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
// 打开的窗口,那么程序会重新创建一个窗口。
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit();
});
// 在这个文件中,你可以包含应用程序剩余的所有部分的代码,
// 也可以拆分成几个文件,然后用 require 导入。
preload.js
开发electron的第一个疑问是如何使用nodejs能力,
有两种方式
|—直接在渲染层中使用,也就是上面index.html包含或者引用的js中
|—主进程创建window时注入preload.js文件,在preload.js中注册一些nodejs能力,然后在渲染层使用
前者由于有安全隐患,这里推荐使用后者,
但是前者这种渲染层直接使用nodejs能力却是另一个桌面端框架的特色,
nwjs: https://nwjs.io/ ,nwjs目前维护不是很好,就不推荐了
下述preload.js的示例中就使用了nodejs的process模块
// preload.js
// 所有Node.js API都可以在预加载过程中使用。
// 它拥有与Chrome扩展一样的沙盒。
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector);
if (element) element.innerText = text;
};
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency]);
}
});
package.json
这个文件比较简单,依赖了electron,添加了一个脚本,
"devDependencies": {
"electron": "^21.1.1",
},
"scripts": {
"start": "electron ."
}
可以通过执行npm start看效果了,效果如下

你没看错,简单的几个文件,已经可以在桌面端跑起来一个桌面应用了
小结
上面的hello world示例,蕴含了electron最核心的原理
1.index.html代表渲染进程,后续会使用html,css,js开发渲染进程(UI层),这里你可以使用任何前端技术栈,例如react,vue等,总体来说类似开发网页
2.main.js代表主进程,操作系统一些特性,应用各种事件监听等,都在这里实现,这里更多的是要熟悉electron的api,因为electron磨平了操作系统特性
3.preload.js代表nodejs能力,所有渲染层需要使用到的nodejs能力,建议都封装到preload.js中,这里更多的是需要熟悉nodejs能力
4.这里还差一个主进程和渲染进程通信能力
熟悉了以上4个能力,基本就掌握了electron的开发,所以可以多看看这个示例~
【electron-实践】
由于需要一篇文章学会electron,只讲概念比较枯燥,
这里引入一个实践项目,跟着实践项目,掌握概念比较快,
之前开发了一个网页版的todolist,
这里在这个项目的基础上继续深入,
了解之前的项目可以看这两篇文章:
上述hello world代码详见: https://github.com/uikoo9/dishi-monorepo/tree/dishi-electron%400.0.3
在packages/dishi-electron文件夹下,

electron-抽取src
这里需要注意,electron是作为dev依赖引入的,如下
"devDependencies": {
"electron": "^21.1.1"
},
这是因为electron的开发和构建有些不同
开发时:依赖electron这个npm包
构建时:会将electron相关能力打入包内,而不直接使用electron这个npm包
所以对如上代码,需要抽取一个src文件夹,只放electron相关业务代码,如下

这里可以看到抽取了一个src文件夹放index.html,main.js,preload.js,以及package.json
外层也有一个package.json,和src内的package.json不一样
外层package.json内容如下:
负责提供开发时的electron环境,以及后续的打包事宜
{
"name": "dishi-electron",
"private": true,
"version": "0.0.4",
"devDependencies": {
"electron": "^21.1.1"
},
"scripts": {
"start": "electron electron-src"
}
}
内层package.json内容如下:
负责electron应用的业务代码,主要是提供入口main.js的位置
{
"name": "dishi",
"private": true,
"version": "0.0.3",
"main": "main.js"
}
以上代码详见: https://github.com/uikoo9/dishi-monorepo/tree/dishi-electron%400.0.5
【electron-Mac打包】
上面已经用electron跑起一个本地的桌面应用了,
本地跑起应用后,是不是迫不及待想构建打包,发给小伙伴们看看?
桌面应用的打包分发分为两步:
1.构建好对应操作系统的应用,比如mac下的app,windows下exe等
2.将构建好的应用程序打成安装包,最终对外分发安装包
这里先进行第一步生成应用,
官方推荐的打包工具是:https://www.electronforge.io/
这里使用另一个工具进行打包,或者说生成应用, https://github.com/electron/electron-packager
由于后续打包相关的工作比较多,封装了一个打包工具, https://qiao-electron-cli.vincentqiao.com/#/
qiao-electron-cli:安装
先安装这个工具,如下,这个工具是打包使用,所以也是安装在外层文件夹
npm i -D qiao-electron-cli
qiao-electron-cli:配置
然后在src同级创建一个pack文件夹,引入一个配置文件qiao-electron.config.js,如下
每个字段都有备注,要关注的几个字段
arch:平台类型,例如我是mac非m1,选择x64
srcPath,srcFiles:就是上一步抽取src的文件夹,和最终桌面应用需要使用哪些文件,
distPath:会将src下的文件复制到dist,这是为了减少包体积,避免打入一些无用文件
// 主进程下的package.json
const srcPkg = require('../src/package.json');
// config
let config = {
// app环境,online,test之类的,会拼接在dmg安装包名上
appEnv: 'online',
// app名称,默认从主进程下的package.json中获取
appName: srcPkg.name,
// app版本号,会显示在dmg安装包名以及关于面板上,默认从主进程下的package.json中获取
appVersion: srcPkg.version,
// app应用图标
appIconPath: 'pack/static/icon/icon.icns',
// app权限声明,会显示在关于面板上
appCopyright: 'Copyright © 2022 dishi版权所有',
// app操作系统,详见https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html#arch
arch: 'x64',
// app应用包中的app文件夹是否使用asar格式,默认为false
asar: false,
// app中主进程src路径
srcPath: 'src',
// 最终要打包到app应用包中的文件和文件夹,在dist这一步会复制出去
srcFiles: ['index.html', 'main.js', 'preload.js', 'package.json'],
// srcFiles中的文件和文件夹会复制到这个目录
distPath: 'dist',
// app应用包及dmg安装包生成的路径
outPath: 'out',
// app安装包dmg中的背景图
dmgBackground: 'pack/static/bg.png',
};
// qe config
module.exports = config;
qiao-electron-cli:dist
qiao-electron-cli的dist命令,作用是将src中配置好的文件复制到dist文件夹,避免打包后引入多余的文件
执行这个命令
"dist": "qelectron d ./pack/qiao-electron.config.js",
效果如下

qiao-electron-cli:packmac
packmac用来生成mac下的应用文件,同样可以在外层package.json中加入下面这些命令
"dist": "qelectron d ./pack/qiao-electron.config.js",
"prepackmac": "npm run dist",
"packmac": "qelectron pm ./pack/qiao-electron.config.js",
执行npm run packmac,效果如下

可以看到执行命令后,src同层级,生成了dist文件夹存放待打包文件,out文件夹存放生成好的mac应用,

out文件下的dishi app已经是可以双击打开的mac应用了,

打开后的效果如下

以上代码见: https://github.com/uikoo9/dishi-monorepo/tree/dishi-electron%400.0.6
qiao-electron-cli:icon
可以看到上面跑起来的mac应用的图标是这样,右侧的Qz图标

这个图标是复用之前另一个应用了,这里做下定制,
之前pack/qiao-electron.config.js配置中有icon属性,如下
// app应用图标
appIconPath: 'pack/static/icon/icon.icns',
这里可以看到app icon需要一个icns文件,
icns的生成比较麻烦,所以qiao-electron-cli工具做了封装
qiao-electron-cli的icon命令用来将普通png生成为icns文件,
需要准备一个512*512的图,放到pack/static/icon/pic.png这个位置,
然后执行如下命令
"icon": "qelectron icon ./pack/static/icon/pic.png",
效果如下

同时会在icon文件夹下生成icns文件,

再次执行npm run packmac,打开应用查看图标,如下

由于修改的代码都是ignore的部分,所以代码还是这个版本:https://github.com/uikoo9/dishi-monorepo/tree/dishi-electron%400.0.6
qiao-electron-cli:packdmg
生成本地应用,和定制mac图标后,接着是进行打包了,
执行下面的命令
"packdmg": "qelectron pd ./pack/qiao-electron.config.js",
即可生成dmg文件,如下

打开dmg后,效果如下

这里左侧显示滴石是qiao-electron.config.js中的
appName: '滴石',
左侧以及title上显示的icon是qiao-electron.config.js中的
appIconPath: 'pack/static/icon/icon.icns',
整个界面的背景图是qiao-electron.config.js中的
dmgBackground: 'pack/static/bg.png',
打开dmg的同时在mac桌面会生成如下图标,这个是安装包的图标

按图示将滴石DS拖动到右侧applications中,后在应用程序中就可以找到运行的图标了

运行后的效果

以上代码详见: https://github.com/uikoo9/dishi-monorepo/tree/dishi-electron%400.0.7
qiao-electron-cli:uploaddmg
生成dmg后为了方便分发,可以上传到一些文件服务器,比如cos,
qiao-electron-cli中封装了上传到cos的命令uploaddmg,
需要在qiao-electron.config.js中配置cos的config,如下
// cos config,可以配置cos,直接上传到cos上
const cosConfig = require('./config.json');
config.cosConfig = {
SecretId: cosConfig.SecretId,
SecretKey: cosConfig.SecretKey,
Region: cosConfig.Region,
Bucket: cosConfig.Bucket,
destPath: '21_dishi/dmg',
};
然后执行如下命令即可
"uploaddmg": "qelectron ud ./pack/qiao-electron.config.js"
以上代码详见: https://github.com/uikoo9/dishi-monorepo/tree/dishi-electron%400.0.8
【electron-Mac定制】
到这里学习到了这些内容:
1.electron:简介
2.electron:安装
3.electron:helloworld
4.electron:打包mac应用
5.electron:定制mac图标
6.electron:打包mac安装包
web项目
以上内容都是基于hello world,下面基于之前的一个web项目继续,
地址: https://insistime.com/dishi-indexeddb
文章: https://blog.insistime.com/indexeddb
代码: https://github.com/uikoo9/dishi-monorepo/tree/indexeddb
这个web项目基本是一个本地项目,基于本地的IndexedDB,
所以比较好内嵌到electron中,
通过dishi-web项目下的webpack,新建一个webpack配置文件,将项目构建到dishi-electron/src/renderer下

主进程文件调整
由于src下新增了renderer文件夹,对应的新增一个main文件夹,用来存放主进程代码
主进程这里引入另一个工具npm包,方便使用electron的能力, https://code.insistime.com/#/qiao-electron
调整后如下

自定义菜单和关于面板
可以看到之前滴石启动后的菜单是默认的,如下

这里通过qiao-electron的方法,即可自定义菜单,代码如下,在src/main/init/init-menu.js中
'use strict';
// q
const { setApplicationMenu, setAboutVersion } = require('qiao-electron');
// version
const { version } = require('../../package.json');
// set application menu
setApplicationMenu();
// set about version
setAboutVersion(version);
定制后效果如下




自定义标题栏
目前标题栏效果如下

看着比较丑,可以在创建window时添加这个属性隐藏标题栏
titleBarStyle: 'hiddenInset',
隐藏后效果如下

但是顶部不可拖动了,需要修改下html和css
html添加一个title,css也做对应修改

这样顶部就可以拖动了,并且隐藏了标题栏
最终效果如下

以上代码详见: https://github.com/uikoo9/dishi-monorepo/tree/dishi-electron%400.1.2
【electron-进程和通信】
上面有说到electron分为主进程和渲染进程,
渲染进程,开发ui
主进程,处理操作系统相关事情
这里用mac下的活动监视器查看这个项目跑起来后的进程,
由于应用名称叫滴石,直接搜索滴石即可,如下,

其中第一个就是主进程,renderer的就是渲染进程
这里演示一下简单的进程通信,例如渲染进程从主进程拿到应用的版本号
主进程通过ipcMain做监听,如下
ipcMain.handle(IPC_APP_GET_VERSION, () => {
return version;
});
渲染进程通过ipcRenderer获取主进程的版本号,如下
/**
* appGetVersionIPC
* @returns version
*/
export const appGetVersionIPC = async () => {
return await ipcRenderer.invoke(IPC_APP_GET_VERSION);
};
将上述代码放到window的preload.js文件中,即可在渲染进程的js中调用,如下
const v = await window.electron.appGetVersionIPC();
最终渲染进程中的preload.js提供了获取版本的方法,preload中通过ipc通信获取到主进程返回的版本,效果如下

以上代码详见: https://github.com/uikoo9/dishi-monorepo/tree/dishi-electron%400.1.4
【qiao-electron】
qiao-electron
1.https://code.insistime.com/#/qiao-electron
2.封装了很多electron常用的能力
qiao-electron-cli
1. https://qiao-electron-cli.vincentqiao.com/#/
2.封装了electron打包相关的能力
【总结】
到这里,用electron开发完了一个mac应用,
以上代码详见: https://github.com/uikoo9/dishi-monorepo/tree/electron
1.electron-简介
2.electron-安装
3.electron-hello world
4.electron-使用qiao-electron-cli的packmac命令打包mac应用
5.electron-使用qiao-electron-cli的icon命令制作mac图标
6.electron-使用qiao-electron-cli的packdmg命令打包mac安装包dmg
7.electron-使用qiao-electron-cli的uploaddmg命令上传dmg到cos
8.electron-使用qiao-electron定制mac菜单
9.electron-使用qiao-electron定制mac关于面板
10.electron-mac定制无标题栏应用
11.electron-进程和通信
12.electron-使用qiao-electron获取app版本号
13.electron-electron专栏: https://blog.csdn.net/uikoo9/category_11468921.html