Electron学习之路 -基础教程篇
以下内容是根据 官网 资料和自己实践整理(查阅资料)所得;
根据官网说明:Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。
创建第一个Electron应用程序
简介》
Electron应用程序是使用npm包建立的。Electron可执行文件应该安装在项目的devDependencies中,并且可以使用package.json文件中中的脚本在开发模式下运行。
可执行文件运行package.json的main属性中的JavaScript入口点。这个文件控制着Electron的主进程,它运行着Node.js的一个实例,负责应用的生命周期,显示本地接口,执行特权操作,以及管理渲染进程。
渲染进程(或简称渲染器)负责显示图形内容。您可以通过将网页指向一个web地址或一个本地HTML文件来将网页加载到渲染器中。渲染器的行为与普通网页非常相似,并且可以访问相同的web api。
学前需知》
通过将Chromium和Node.js嵌入到一个二进制文件中,Electron允许您使用一个JavaScript代码库创建在Windows、macOS和Linux上运行的跨平台应用程序。
Electron是一个原生的web应用包装层,运行在Node.js环境中,因此在进入学习之前需要熟悉Node和前端web开发基础知识:
Getting started with the Web (MDN Web Docs)
与其它桌面程序应用框架的对比》
同样是做桌面应用程序的框架,那么它和传统的Winform和WPF又有什么区别呢?
Winform:上手容易,技术老套,性能一般,常用组件库DevExpress,SunnyUI,适合对性能要求不高的项目
WPF:性能高,可以做炫酷的效果,上手进阶难,适合大型的桌面应用程序
Electron:对前端技术人员比较友好,相当于在网页上套了一层壳,可以打包成exe程序,只是打包后比较臃肿,性能堪忧,不过上手容易,方法都是封装好了的,根据官方可以快速构建一个桌面应用程序。
当然基于c++ 开发的QT框架也能做出漂亮的桌面应用程序。
所以这么看来Eelectron也是挺有优势的,因为是基于web开发来实现跨平台的桌面应用程序,对于如今广大的前端开发用户还是很友好的,当然其性能和对内存的处理以及打包文件的处理是不够好的。
开发工具》
这里我使用的是:Visual Studio Code
这个软件也是基于Electron开发出来的。
创建项目》
1 | mkdir my-electron-app && cd my-electron-app |
使用方式:
这里我是在E盘(你自己的项目存放位置)下的文件搜索框内容输入cmd,
这样打开命令提示符号终端窗口,然后输入
1 | mkdir my-electron-app && cd my-electron-app |
回车
这样就会在E盘下生成 my-electron-app 这个文件夹,并且指令进入此文件夹
接着使用指令
1 | npm init |
这样就是初始化创建了项目的配置文件
1 | package.json |
当然了如果使用了淘宝镜像,可以使用命令
1 | cnpm init |
来替换
1 | npm init |
Electron的安装》
命令
1 | npm install electron --save-dev |
执行以上命令:在初始化包并安装Electron之后,package.json文件应该如下所示。同时文件夹下还生成了node_modules文件夹,其中包含Electron可执行文件,以及一个包锁:.package-lock.json来指定要安装的确切依赖版本
1 | { |
在以上文件(package.json)中的main对应的脚本文件(这里是main.js)
它是Electron应用程序的入口。这个脚本控制主进程,它运行在Node.js环境中,负责控制应用的生命周期,显示本机接口,执行特权操作,以及管理渲染进程(后面会详细介绍)。
这里的main.js是我们在创建应用的时候自己命名的,但是框架并没有为我们生成这个js文件,所以我们需要在根目录下自己去手动生成,以此来保证配置正确运行。这里我们将main.js中的内容设置简单点:
1 | console.log('hello Electron!'); |
程序启动配置》
由于Electron的主进程是在Node.js中运行的,所以您可以使用Electron命令执行任意Node.js代码(甚至可以将其用作REPL)。
要执行这个脚本,就需要需要添加“electron .”到package.json的scripts字段中的start指令中。这个指令将告诉Electron可执行文件在当前目录中查找主脚本,并在dev模式中运行它。这样package.json内容如下所示:
1 | { |
配置完start指令后,通过在终端(cmd)运行指令 npm run start便会在终端打印出“hello Electron!”。
当然这只是在Electron中执行了打印文字的脚本,接下来将学习如何使用HTML创建用户界面并将其加载到本地窗口中。
上面我们实现了通过在终端运行Electron指令–执行入口文件main.js
功能:打印文字;
接下来我们将学习如何在BrowserWindow中加载网页。
根据官网介绍:
In Electron, each window displays a web page that can be loaded either from a local HTML file or a remote web address.
在Electron中,每个窗口都显示一个可以从本地HTML文件或远程web地址加载的网页。
也就是Electron应用程序窗口可以显示本项目中的.html文件,也可以通过加载网站网络地址来实现网页的呈现。
如果是加载网站的url来实现网页的呈现,这种方式就相当于我们把网站开发好了,发布到外网上。然后在Electron中加载进来就可以了,相当于给网站外加了个一个浏览器的壳,这样对前端开发人员来说是相当友好滴。
接下来将介绍如何在Electron框架下的BrowserWindow中加载本地html文件以及已发布网站地址。
1)在BrowserWindow中加载本地(本项目)html文件
我们在根目录下创建一个名为index.html的文件
1 | <!DOCTYPE html> |
那么我们如何将这个网页加载到Electron的浏览器窗口(BrowserWindow)中去呢?
前面我们已经了解到了,package.json配置文件中的main对应的是整个Electron程序的入口文件(这里是:main.js)。
这样我们需要将main.js中的内容修改为以下内容:
1 | const { |
让我们看看以上配置内容的语义。
1 | const { |
以上配置用CommonJS模块语法导入了两个Electron模块:
app:控制整个应用程序事件的声明周期
BrowserWindow:创建和管理应用程序的窗口
这里我们可能已经注意到模块名称的大小写并不是使用统一的规则,这里面是有它自己的规则的,根据官网说明:
Electron在这里遵循典型的JavaScript约定,其中PascalCase模块是可实例化的类构造函数(如BrowserWindow, Tray, Notification),而camelCase模块是不可实例化的(如app, ipcreender, webContents)。
需要注意的是:目前Electron不直接支持ECMAScript模块(即使用import来加载模块)。
1 | const createWindow = () => { |
以上配置表示:通过函数createWindow()加载你的网页到一个新的BrowserWindow实例;
也就是说窗口BrowserWindow可以被我们重写的,这样我们可以控制它的大小、显示的位置、是否显示自带的辅助功能菜单(File、Edit、View、Window、Help)以及是否显示最小化,最大化和关闭图标等功能。
1 | app.whenReady().then(() => { |
以上配置表示:应用程序执行时候就调用我们定义的函数createWindow()(重写BrowserWindow)
Electron的许多核心模块都是Node.js事件触发器,它们遵循Node的异步事件驱动架构。app模块就是其中一个发射器。
在Electron中,BrowserWindows只能在app模块的ready事件触发后创建。
你可以通过使用app.whenReady() API以及等这个事件的promise执行完后来回调createWindow()
2)在BrowserWindow中加载网站网络URL来实现网页的呈现
在以上内容的基础上,我们只需要将配置:
1 | win.loadFile('index.html') |
修改为
1 | win.loadURL('https://www.romanticlife.top') |
这样就可以在BrowserWindow调用已发布的网站(本博客)
现在我们执行指令
1 | npm run start |
此时我们看到index.html文件加载到窗口(BrowserWindow)中去了。
至此,Electron应用程序是已经能够正常的运行起来了,那么我们如如何去管理应用程序的窗口生命周期喃?
管理应用程序的声明周期》
Electron应用程序在窗口中显示的每个网页将运行在一个单独的进程中,称为渲染进程(或简称为渲染器)。
这个渲染过程与通用网页的渲染过程是一致的,即渲染过程可以访问与典型前端web开发相同的JavaScript api和工具,例如使用webpack来捆绑和缩减代码,或者使用React来构建用户界面…
应用程序窗口在每个操作系统上的行为都不同。比起默认地强制执行这些约定,Electron给你在应用程序代码中实现它们的选择,如果你希望遵循它们。你可以通过监听由应用程序和BrowserWindow模块发出的事件来实现基本的窗口约定。
检查Node的进程。平台变量可以帮助您在特定平台上有条件地运行代码。
注意
Electron只能在三种可能的平台上运行:win32 (Windows)、linux (linux)和darwin (macOS)。
关闭所有窗口时退出应用 (Windows & Linux)》
注意这里只是针对:window和Linux系统,macOS系统不适用。
在Windows和Linux上,关闭所有窗口通常会完全退出应用程序。如果要在Electron应用程序中实现这个模式,需要监听app模块的window-all-closed事件,并调用app.quit()来退出你的应用程序
1 | app.on('window-all-closed', () => { |
这样我们就是实现了一个Electron应用程序从创建》运行》关闭,整个生命周期。完整的代码如下:
1 | package.json |
项目调试
如果你想在VS Code工具中调试Electron应用程序,则需要将VS Code附加到主进程和渲染进程。
以下是要运行的示例配置:
在你的项目下新建一个.vscode文件夹,并在其下创建一个launch.json配置文件:
1 | { |
做了以上工作后,当你选择侧边栏的“运行和调试”时,会出现“Main + renderer”选项,这将允许你设置断点并检查主进程和渲染进程中的所有变量。
在以上launch.json文件中有3个配置项:
1)Main用于启动主进程,并开放9222端口用于远程调试(–remote-debugging-port=9222)。这是我们将用于监听Renderer调试器的端口。因为主进程是一个Node.js进程,类型被设置为pwa-node (pwa-是用来告诉VS Code使用最新的JavaScript调试器的一个前缀)。
2)Renderer用于调试渲染器进程。由于这个进程(Renderer)是由主进程创建的,所以我们必须“附加”到主进程上(“request”:“attach”),而不是创建一个新的进程。渲染器进程是一个web进程,所以不得不使用pwa-chrome作为调试器。
3)Main + renderer是一个复合任务,可以同时执行前面的任务。
因为我们是在Renderer中附加一个进程,所以有可能你的代码的前几行会被跳过,因为调试器在它们被执行之前没有足够的时间进行连接。可以通过刷新页面或在开发模式执行代码之前设置settimeout来解决这个问题。
【知识延申】》如何给Chrome浏览器开启远程调试的功能?
。找到Chrome浏览器的安装地址(在Chrome浏览器的搜索栏输入:chrome://version 出现页面信息中的“可执行文件路径”项即是当前电脑中Chrome浏览器的安装地址);
。关闭所有打开的Chrome浏览器
。在地址栏输入cmd,打开命令终端
。在命令终端中输入:chrome.exe –remote-debugging-port=9222
按照以上操作后便会自动打开一个网页:http://127.0.0.1:9222/json/version
页面中的webSocketDebuggerUrl后面的地址,这就是我们远程链接的地址;测试的过程中,可能会发现标签页越开越多。只要至少保留一个标签页不关闭,那么这个浏览器窗口就可以一直使用。
当然如果你想在调试方面有更深入的了解,下面的指南提供了更多的信息:
预加载脚本:preload.js
接下来将了解什么是预加载脚本,以及如何使用预加载脚本安全地将私有api公开到渲染进程中以及如何使用Electron的进程间通信(IPC)模块在主进程和渲染进程之间通信。
什么是预加载脚本?
预加载脚本中的代码在网页加载到浏览器窗口之前运行。它可以访问DOM API和Node.js环境,通常通过contextBridge API向渲染器暴露私有API。
因为主进程和渲染进程的职责有很大不同,Electron应用程序经常使用预加载脚本建立进程间通信(IPC)接口,在两种进程之间传递任意消息。
Electron的主进程是一个具有完全操作系统访问权限的Node.js环境。在Electron模块之上,你还可以访问Node.js内置模块,以及任何通过npm安装的包。另一方面,由于安全原因,渲染进程运行网页时默认不运行Node.js。
为了将Electron的不同过程类型连接在一起,我们需要使用一种称为预加载的特殊脚本。
接下来了解使用预加载脚本增强渲染器。
BrowserWindow的预加载脚本运行在可以访问HTML DOM和Node.js环境的上下文中。在渲染器加载页面之前会注入预加载脚本,类似于Chrome扩展的内容脚本。若要向渲染器添加需要特权访问的特性,可以通过contextBridge API定义全局对象。
为了演示这个概念,我们创建一个预加载脚本,实现将应用程序的Chrome、Node和Electron版本暴露到渲染器中。
新建preload.js脚本,用于暴露Electron进程的选定属性。在Versions全局变量中向渲染器进程添加Versions对象。内容如下:
1 | const { contextBridge } = require('electron') |
要将这个脚本附加到你的渲染器进程,需要将它的路径传递给BrowserWindow构造函数中的webPreferences.preload项:
1 | /*main.js */ |
在以上内容中我们用到了两个Node.js的知识点:
1)__dirname 字符串指向当前正在执行的脚本的路径(本例中即为项目的根文件夹)。
2)path. join 接口将多个路径拼接在一起,组成一个跨所有平台的组合路径字符串。
此时,渲染器可以访问全局versions,接着让我们在窗口中显示该版本信息。这个变量可以通过window.versions或versions访问。
新建一个renderer.js脚本,用于通过 document.getElementById DOM API实现将 id为info的HTML元素的文本信息替换为版本信息:
1 | /*renderer.js*/ |
然后修改index.html,添加一个id属性为info的新元素,并引入renderer.js脚本:
1 | <!DOCTYPE html> |
完成以上步骤后,运行程序我们可以看到窗口显示出Chorm、Node.js、Electron的版本信息。
进程间通信
正如上面所提到的那样,Electron的main和renderer进程职责不同且不可互换。这意味着不能从渲染进程直接访问Node.js api,也不能从主进程访问HTML文档对象模型(DOM)。
解决这个问题的方法是使用Electron的ipcMain和ipcrederer模块进行进程间通信(IPC)。如需从你的网页向主进程发送消息,你可以用ipcMain.handle句柄建立一个主进程,然后在预加载脚本中暴露一个调用ipcRenderer.invoke的函数去触发这个句柄。
为了演示这个观点,我们将在渲染器中添加一个名为ping()的全局函数,该函数将从主进程返回一个字符串。
首先,在预加载脚本中配置调用:
1 | const { |
IPC安全机制》
请注意,我们为何将ipcRenderer.invoke(‘ping’) 调用封装在一个帮助函数中,而不是直接通过上下文桥接暴露ipcRenderer模块的。你绝对不想通过preload直接暴露整个ipcRenderer模块。这将使渲染器能够向主进程发送任意IPC消息,并将为恶意代码提供有力的攻击载体。
然后,在主进程中设置handle 侦听器,此操作需要在加载HTML文件之前执行,以确保在渲染器进程发出invoke调用之前handle 侦听器已准备就绪:
1 | /*main.js */ |
设置好发送方和接收方之后,现在就可以通过刚才定义的“ping”通道从渲染器向主进程发送消息了。
1 | /*renderer.js*/ |
若要对ipcRenderer 和ipcMain模块更深入理解,查看完整的Inter-Process Communication指南。
接下来将了解为应用添加更多功能的环境,又如何将应用分发给用户。
添加功能
即增加应用程序的复杂性。
如果您一直按照本文进行操作,至此,您应该拥有一个具有静态用户界面的功能性Electron应用程序。从现在开始,你可以从两个大的方向来提升你的应用:
1)增加渲染进程的web应用程序代码的复杂性
2)使操作系统和Node.js的深度集成
理解这两个宽泛概念之间的区别是很重要的。
对于第一点,Electron对特殊环境是非必要的。在Electron中构建一个漂亮的待办事项列表的应用程序只是将你的Electron BrowserWindow指向一个漂亮的待办事项列表web应用程序。最终,你正在使用与在web上相同的工具(HTML、CSS、JavaScript)构建你的渲染器UI。因此,Electron的文档不会深入讨论如何使用标准的web工具。
另一方面,Electron还提供了一组丰富的工具,允许您与桌面环境集成,从创建托盘图标到为显示本地菜单添加全局快捷方式。它还在主进程中为您提供了Node.js环境的所有功能。这一功能集将Electron应用程序与在浏览器选项卡中运行网站分开,并且是Electron文档的重点。
Electron的文档中有许多教程,可以帮助您了解更高级的话题和更深入的操作系统集成。要开始使用,
请查看How-To Examples文档。
接着继续了解如何使Electron应用程序让最终用户使用?
为此我们将学习使用Electron Forge打包和分发应用程序的基础知识。
应用程序打包
Electron没有任何打包和分发的工具捆绑到其核心模块中。一旦你有了一个在开发模式下工作的Electron应用程序,你需要使用其他工具来创建一个打包的应用程序,你可以分发给你的用户(也称为分发版)。分发文件可以是安装程序(如Windows上的MSI),也可以是可移植的可执行文件(如macOS上的.app)。
打包项目》
将 Electron Forge 添加到您应用的开发依赖中
electron-forge 相当于 的一个脚手架,可以让我们更方便的创建、运行、打包 项目
注意:electron-forge打包时是需要安装git的;
因为它在打包时会检测系统有没有装git,没有是打不了包的 。
并使用其”import”命令设置 Forge 的脚手架
1 | cnpm install @electron-forge/cli -g |
以上两条命令执行成功后会在项目中package.json文件scripts节点中自动新增以下内容:
1 | "package": "electron-forge package", |
执行
1 | npm run package |
会在根目录下生成可执行程序.exe文件夹out。
执行
1 | npm run make |
包含以下2个步骤:
1)首先它将引用线下运行electronic -forge包,将您的应用程序代码与Electron二进制文件捆绑在一起。打包的代码将生成到一个文件夹中。
2)然后它将使用这个打包的app文件夹为每个配置的制造者创建一个单独的分发版。npm run make命令执行后,会在根目录下生成一个out文件夹,其中包含分发版和一个包含打包的应用程序代码(.exe)的文件夹。
这样,out/make文件夹中的分发版应该可以启动了
至此您已经创建了第一个捆绑的Electron应用程序。
以上我们已经成功生成了可执行文件,如何修改它exe可执行程序的图标、程序名称(xxx.exex)。。。???
配置程序图标和名称等》
这是可以在打包脚本中去配置的:
1 | "package":"electron-packager . appName --platform=win32 --arch=x64 --icon=favicon.ico --out=./out --asar --app-version=1.0.1 --overwrite --ignore=node_modules", |
.appName
表示生成的exe可执行文件的名称是appName,即:appName.exe
–icon=favicon.ico
设置可执行文件的图标是favicon.ico,这个图标文件是我们已手动放在根目录下
–out=./out
指定打包文件输出的文件夹位置,当前指定的为项目目录下的out文件夹
–asar
该参数可以不加(一般需要加上),如果加上,打包之后应用的源码会以.asar格式存在;否则会以文件夹app形式存在,app中呈现源码
–app-version=1.0.1
设置项目程序的版本号
–overwrite
表示此次打包会覆盖上次打包的内容
–ignore=node_modules
指定打包时忽略目录:node_modules
发布与更新
如果您一直按照本教程的流程学习,这是本教程的最后一步!在这小结,你将发布你的应用到
上,并将自动更新集成到你的应用代码中。使用update.electronjs.org
Electron的维护者在 https://update.electronjs.org/ 上为开源应用程序提供免费的自动更新服务。它的要求是:
1)应用运行在macOS或Windows上
2)应用程序有一个公共的GitHub库
3)构建版本发布到GitHub releases
4)构建是代码签名的(code signed)
发布GitHub release
Electron Forge拥有 Publisher 插件,可以自动将打包的应用程序分发到各种来源。在本教程中,我们将使用GitHub Publisher,它将允许我们将我们的代码发布到GitHub上。
生成个人访问令牌
Forge不能在未经允许的情况下发布到GitHub上的任何存储库。你需要传入一个经过验证的令牌,让Forge能够访问你的GitHub发行版。最简单的方法是使用 public_repo作用域 create a new personal access token (PAT),它提供对公共存储库的写访问。一定要保密这个标志。
设置GitHub发布者
安装模块
Forge的 GitHub Publisher 是一个需要安装在你的项目的devDependencies中的插件:
1 | npm install --save-dev @electron-forge/publisher-github |
或
1 | yarn add --dev @electron-forge/publisher-github |
在Forge中配置发布者
一旦您安装了它,您需要在您的Forge配置中设置它。一个完整的选项列表记录在Forge的 PublisherGitHubConfig API文档中。
1 | /*package.json*/ |
设置身份验证令牌
您还需要让发布服务器知道您的身份验证令牌。默认情况下,它将使用存储在 GITHUB_TOKEN环境变量中的值。
运行发布指令
把Forge的 publish command 添加到你的npm脚本中:
1 | /*package.json*/ |
或
1 | yarn run publish |
默认情况下,这只会为您的主机操作系统和体系结构发布一个发行版。你可以通过传递–arch 标志到你的Forge命令来发布不同的架构。
这个版本的名称将对应于项目文件package.json中的version 字段。
在GitHub Actions中发布
在本地发布是很痛苦的,特别是因为你只能为你的主机操作系统创建分发版(也就是说,你不能从macOS发布windows.exe文件)。
一个解决方案是通过自动化工作流发布你的应用程序,比如 GitHub Actions,它可以在Ubuntu、macOS和Windows上运行云中的任务。这正是 Electron Fiddle 所采用的方法。你可以参考Fiddle
的 Build and Release pipeline 以及 Forge configuration 来了解更多细节。
测试更新程序代码
现在我们有了一个通过GitHub releases的功能发布系统,我们现在需要告诉我们的Electron应用程序在新发布的时候下载更新。Electron应用程序通过 autoUpdater 模块实现这一功能,该模块从更新服务器提要读取数据,以检查是否有新版本可供下载。
update.electronjs.org服务提供了一个与更新程序兼容的提要。例如,Electron Fiddle v0.28.0将在https://update.electronjs.org/electron/fiddle/darwin/v0.28.0 上检查端点,以查看是否有更新的GitHub版本可用。服务提供了一个与更新程序兼容的提要。例如,Electron Fiddle v0.28.0将在https://update.electronjs.org/electron/fiddle/darwin/v0.28.0 上检查端点,以查看是否有更新的GitHub版本可用。
在你的版本发布到GitHub后,update.electronjs.org服务应该可以为你的应用程序工作。剩下的唯一步骤是使用autoUpdater 模块配置提要。
为了使这个过程更容易,Electron团队维护 update-electron-app 模块,该模块在一个函数调用中为update.electronjs.org设置autoUpdater 样板文件——不需要配置。此模块将搜索update.electronjs.org feed,此与项目 package.json文件中”repository”字段匹配。
首先,将安装作为运行时依赖项的模块:
1 | npm install update-electron-app |
或
1 | yarn add update-electron-app |
然后,导入模块并在主进程中立即调用它:
1 | /*main.js*/ |
这样,一旦打包应用,它会为你发布的每一个新的GitHub版本实现自我更新。
在本小结中,我们配置了Electron Forge的GitHub发布者上传你的应用程序的分发到GitHub发行版。由于分发版不能总是在平台之间生成,如果您无法访问机器,我们建议在持续集成管道中设置您的构建和发布流程。
Electron应用程序可以通过将autoUpdater模块指向更新服务器feed进行自我更新。update.electronjs.org是Electron为GitHub上发布的开源应用提供的一个免费更新服务器。配置您的Electron应用程序来使用该服务就像安装和导入update-electron-app模块一样简单。
如果您的应用程序不符合update.electronjs.org的条件,您应该部署自己的更新服务器,并自己配置autoUpdater模块。
问题集锦》
1)命令cnpm install electron -g 是全局安装,用于在第一次安装后出现‘electron’不被识别的情况,安装成功了,使用:electron -v命令可以查看版本号。
当然了使用这个命令前需要添加淘宝镜像;具体可参照前面创建Vue项目里的操作方法,也可自行百度。
这里我在使用这个命令时出现了以下错误:
Error: EPERM: operation not permitted, mkdir ‘D:\softprogram\nodejs\node_modules\electron_tmp’
原因是:node权限问题;
解决办法:这里我们进入:D:\softprogram\nodejs》右键》属性》安全》点击“编辑”》选中“Users”》勾选权限框 “允许”栏中的所有权限》应用》确定
再执行命令:cnpm install electron -g,即可成功安装。