
前言
Electron是由Github开发的, 是一个跨平台的桌面应用开发框架,用HTML css js 的技术开发桌面上面可以安装的软件,可以让前端人员用HTML css js 的技术开发跨平台可以安装的桌面软件。
像我们前端常使用的开发工具VSCode Atom都是由Electron开发的,因为好奇的我尝试了下开发入门,记录下。
环境搭建
由于是用react所以我直接使用了官方的脚手架create-react-app,以及相关环境node就不再赘述(百度一下都有)。
主要记录下集成Electron配置开发环境。
-
安装
Electron依赖npm i electron --dev -
添加
Electron必要的启动文件在
public文件夹下创建electron.js文件const { app, BrowserWindow } = require('electron') let mainWindow function createWindow () { //创建浏览器窗口 mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, // 是否集成 Nodejs }}) // 判断是开发环境还是生产环境(生产环境是打包后所以直接引用html文件官方的案例就是这样的) process.env.NODE_ENV === 'development' ? mainWindow.loadURL('http://localhost:3000/') : mainWindow.loadURL(`file://${__dirname}/index.html`) // 打开开发者工具,默认不打开 mainWindow.webContents.openDevTools() // 关闭window时触发下列事件. mainWindow.on('closed', function () { mainWindow = null }) } // 当 Electron 完成初始化并准备创建浏览器窗口时调用此方法 app.on('ready', createWindow) // 所有窗口关闭时退出应用. app.on('window-all-closed', function () { // macOS中除非用户按下 `Cmd + Q` 显式退出,否则应用与菜单栏始终处于活动状态. if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', function () { // macOS中点击Dock图标时没有已打开的其余应用窗口时,则通常在应用中重建一个窗口 if (mainWindow === null) { createWindow() } }) -
配置
Electron入口 以及运行开发环境在
package.json文件中添加, 以及添加script配置"main": "public/electron.js", "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "electron:dev": "cross-env NODE_ENV=development electron ." }然后在先启动
npm start后在运行npm run electron:dev就能在界面中显示
-
优化开发启动命令
安装同时开启多个监听服务
concurrently以及wait-on等待的包npm i concurrently wait-on -D然后修改
script文件下的electron:dev命令"electron:dev": "concurrently \"cross-env BROWSER=none yarn start\" \"wait-on http://localhost:3000 && cross-env NODE_ENV=development electron . \""这样我们直接运行这个命令就直接打开的就是
Electron的开发环境直接打开应用,省去了直接需要运行两个命令。
主进程与渲染进程
-
介绍
Electron运行package.json的main脚本的进程被称为主进程。 在主进程中运行的脚本通过创建web页面来展示用户界面。 一个Electron应用总是有且只有一个主进程。由于
Electron使用了Chromium来展示web页面,所以Chromium的多进程架构也被使用到。 每个Electron中的 web 页面运行在它的叫渲染进程的进程中。在普通的浏览器中,web页面通常在沙盒环境中运行,并且无法访问操作系统的原生资源。 然而
Electron的用户在Node.js的API支持下可以在页面中和操作系统进行一些底层交互。
-------- 摘自官网
-
不同进程的特点:
-
Main Process:
- 可以使用和系统对接的
Electron API - 创建渲染进程 -
Renderer Process - 全面支持
Node.js - 有且只有一个
- 可以使用和系统对接的
-
Renderer Process:
- 可以有多个,每个对应一个窗口
- 每个都是一个单独的进程
- 全面支持
Node.js&DOM API - 可以使用一部分
Electron的API
主进程使用
BroswerWindow实例创建网页。每个BroswerWindow实例都在自己的渲染进程里运行着一个网页。当一个BroswerWindow实例被销毁后,相应的渲染进程也会被终止。主进程管理所有页面和与之对应的渲染进程。每个渲染进程都是相互独立的,并且只关心他们自己的网页。由于在网页里管理原生GUI资源是非常危险而且容易造成资源泄露,所以在网页面调用GUI相关的APIs是不被允许的。如果你想在网页里使用GUI操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的GUI操作。 在Electron,我们提供用于在主进程与渲染进程之间通讯的ipc模块。并且也有一个远程进程调用风格的通讯模块remote。 -
进程之间的通讯
在渲染中引入Electron(就是所指的子页面)
const { ipcRenderer } = window.require('electron')
ipcRenderer.send('emit', { name: 'electron' }) // 第一个参数是标识,第二个是携带的参数
在主线程也是相关原理引入ipcMain模块我这项目指的是我的electron.js
const { ipcMain } = require('electron')
ipcMain.on('capture-screen', (e, data) => {
console.log(data) // 这里的data就是渲染进程传过来的
})
这里的主线程与渲染进程的通信更像是一种监听的模式, 一个send另个on。
BrowserWindow Api使用记录
因为这个API都会有所用到所以记录下些主要的参数
const { BrowserWindow } = require('electron')
let mainWin
mainWin = BrowserWindow({
width: 1080, // 所指窗体宽度
height: 800, // 所指窗体高度
backgroundColor: "#292B35", //窗体背景颜色
fullscreenable: false, // 是否全屏
transparent: false, //是否透明
resizable: false, // 是否可以放大缩小
alwaysOnTop:true, // 是否显示在所有窗体中最上层
hasShadow: false, //是否有阴影
frame: false,// 其实就是否显示边框菜单栏标题之类的
x: 0, // x轴坐标(是以显示器左上角为起始点)
y: 0, // y轴坐标
//titleBarStyle: "hidden",
darkTheme:true, // 是否为深色主题窗口
show:false, // 是否在创建的时候显示
title: '', // 窗体的标题
icon: path.join(__dirname, '../win_favicon.ico'), // 窗体的标题ico (这个只在在windows下有效)
webPreferences: {
nodeIntegration: true, // 是否集成 Nodejs
}})
这里想说下titleBarStyle这个参数


暂时只是发现了mac下有效果。
更多详尽的API可上官网查询。官网BrowserWindow相关Api
Tray Api使用记录
因为项目中使用到了托盘特此记录下,碰到windows与mac下区别。
在主线程中引入Tray模块,并创建使用(注意是在app的ready后创建)当Electron完成初始化完成
const { app, Menu, Tray, ipcMain } = require('electron')
let mainTray = null
// 托盘
if(require('os').platform() === 'win32') { // 判断系统类型(因为这里ico文件只支持window下)
mainTray = new Tray(path.join(__dirname, 'win_favicon.ico')); // windows下图标
} else {
mainTray = new Tray(path.join(__dirname, 'logo_normal.png')); // mac设置托盘的图标
mainTray.setPressedImage(path.join(__dirname, 'logo_pressed.png'))// mac设置托盘点击下的图标
}
// 这里是我项目主要的托盘菜单直接贴了这里使用到了Menu Api创建菜单
const contextMenu = Menu.buildFromTemplate([
{label: '截图识字', click: () => {captureSceen()}},
{label: '选择图片', click: () => {uploadImgDialog()}},
{label: '选择PDF文件', click: () => {uploadPdfDialog()}},
{label: '批量识别', click: () => { mutileWin.show()}},
{label: '设置', click: () => {settingWin.show()}},
{label: '识别历史', click: () => {redcordWin.show()}},
{label: '退出', click: () => {
//这里说退出说明下,因为mac下退出会自动执行Tray的destroy所以不需要手动销毁,不然的话mac下点击退出都会崩溃
require('os').platform() === 'win32' ?
mainTray.destroy()
:
MacShowResultWin.destroy()
mutileWin.destroy()
settingWin.destroy()
redcordWin.destroy()
app.quit()
}},//真正的退出(这里直接强制退出)
])
mainTray.setContextMenu(contextMenu) // 将菜单设置到托盘中
// 这个我好像只是在`windows`下看到了效果, 就是点击托盘左键看开主窗体
mainTray.on('click', ()=>{ //模拟桌面程序点击通知区图标实现打开关闭应用的功能
mutileWin.isVisible() ? mutileWin.hide() : mutileWin.show()
mutileWin.isVisible() ? mutileWin.setSkipTaskbar(false):mutileWin.setSkipTaskbar(true); //设置窗口是否在任务栏中
})
在使用Tray的API中碰到的问题
- 图标的文件格式
mac与windows下的区别,最后我也是通过官方的文档查询才发现。所以仔细的阅读官方文档还是很重要的。 - 还有就是
Tray的销毁问题,最终也是网上寻得的答案
在这部分文末贴个官方的Tray文档 官网Tray相关Api
项目打包
-
使用
electron-packager打包1.1 安装相关包
npm install -D electron-packager1.2 直接使用命令打包,
electron-packager的打包基本命令是:electron-packager <location of project> <name of project> <platform> <architecture> <electron version> <optional options>命令说明:
- location of project:项目所在路径
- name of project:打包的项目名字
- platform:确定了你要构建哪个平台的应用(Windows、Mac 还是 Linux)
- architecture:决定了使用 x86 还是 x64 还是两个架构都用
- electron version:electron 的版本
- optional options:可选选项
1.3 在
pacage.json文件得script下添加"electron:build": "electron-packager ./ --all --asar --out=./release-builds --overwrite",最终就可以使用
npm运行该命令进行打包了 -
使用
electron-builder打包(我使用了这种)1.1 安装相关包
npm install -D electron-builder2.1 在
pacage.json文件下添加"build": { "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名 "appId": "com.xxx.xxxxx",//包名 "copyright":"xxxx",//版权 信息 "files": [ // 打包的文件 "package.json", "node_modules/**/*", "build/**/*", "config", "assets" ], "mac": {// mac下 "target": [ "dmg" ], "icon": "public/logo.png" }, "win": { // windows下 "icon": "public/icons/win_favicon.ico", "target": [ { "target": "nsis" // 我们要的目标安装包 } ] } }然后在
script栏添加"pack": "electron-builder --dir"运行
npm run pack就能进行打包了打包后文件很大,自己寻求网上解决方案是删除
package.json的dependencies里的不需要的依赖,然后打后感觉还是很大好像打包后100MB多,不知道有没有大佬有方案能还能缩小吗?不胜感激啊。😉
总结
Electron一个跨端的桌面应用程序开发框架,它发布的那会我就有关注,然后自己也没有实际的尝试过,最近公司有需求,然后自己又有极大的兴趣,所以就三下五除二的开干了,通过此项目自己巩固了下react因为我使用react进行开发, 没有选择vue。因为是平时自己公司的项目都是vue的所以选择了react(vue有专门为Electron写的脚手架electron-vue)。其实个人感觉用Electron开发就是框架搭建好了,然后剩下的都是些网页,还有一些就是网页中(也就是所谓的渲染进程跟主线程的通信)所以不是特别的对系统的一些操作,感觉难度并不是很大的。
然而通过Electron对桌面应用程序的开发,自己对桌面应用也有了相应的了解。然后想想了想前端真的是无所不能啊。😂😂从网页到移动web,然后跨平台APP、跨平台桌面应用程序。
然后感谢 掘金昵称 徐健本尊juejin.cn/post/684490…发布的截图方案因为我自己本项目中使用到了所以写出出处感谢下,还有唯一一点不足的就是双屏截图这个方案也是没有实现,然后我自己也是寻找很多方法也是没有,有大佬有思路的不胜感激寻求。网上好像有说调用原生模块得各种的编译,这种方案我暂时还没有试。第一版本先发了,然后之后优化截图方案。🤞
放上几个Electron的官方文档地址:
Electron(官方文档): www.electronjs.org/
Electron的官方快速开始脚手架: www.electronjs.org/docs/tutori…
Electron-Vue脚手架: simulatedgreg.gitbooks.io/electron-vu…
Electron React Boilerplate: electron-react-boilerplate.js.org/
Electron的 electron-builder打包(官方文档): www.electron.build/
最后放上几张项目图:
Windows:



