掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务

如何利用 Electron 开发一个桌面 APP

你是否曾经想过可以用 HTML、CSS 和 JavaScript 这些前端技术来构建跨平台的桌面应用?

使用 Electron 就能做到。

本文带着你深入 Electron 的核心概念。

阅读本文后,你会知道如何使用 Electron、HTML 和 CSS 构建跨平台桌面应用。

在开始之前,你可以提前看看我们在本教程中要构建的应用。

Hear Me Type [译者注:本文的示例应用名为 Hear Me Type] 功能简单直接,应用中每个键都会播放特有的声音。比如我按下了 “A”,应用会播放字母 A 特有的声音。

该应用目前有两个版本可供下载:本教程的源码,以及一个推荐给更有经验的 Electron 用户的高级版本

因为我还在为改善应用以及添加一些新功能,所以代码会发生变化,请一定注意看看有哪些更新。

就此打住,我们现在开始学习 Electron 并用它来创建第一个应用!

什么是 Electron?

Electron 是一个基于 Chrominum 和 Node.js 的跨平台桌面应用框架。

在这个框架中很容易构建基于 HTML、CSS 和 JavaScript 技术的跨平台应用。构建出来的应用会很好地兼容 Mac、Windows 和 Linux 操作系统。

它还有其它一些特性:

  • 自动更新 —— 应用支持自动更新

  • 原生菜单和通知 —— 可以创建原生应用菜单和上下文菜单

  • 应用崩溃报告 —— 可以将崩溃报告提交到远程服务器

  • 调试和分析 —— Chrominum 的内容模块可以发现性能瓶颈和缓慢的操作。你也可以在应用中使用自己喜欢的 Chrome 开发者工具。

  • Windows installer —— 可以快速便捷地创建安装包。

如果你对 Electron 的功能感到满意,我们就继续深入,创建一个简单的 Electron 应用。

动手之前需要先安装 Node.js。你还应该申请一个 GitHub 账户来保存和更新应用。虽然这个账户并不是必须的,但我非常建议你去申请一个。Github 是一个行业标准,学会使用 Github 非常重要。

我会在教程中使用 Github。

开始

准备好之后,打开系统终端窗口。

按照下面的介绍将 Electron Quick Start 这个 Git 库克隆到计算机上。

我们会基于 Electron Quick Start 来创建自己的软件。

# Clone the Quick Start repository
git clone 
# Go into the repository
cd electron-quick-start
# Install the dependencies and run
npm install && npm start

完成上面的步骤之后,你会看到一个像浏览器窗口的应用打开。它确实是一个浏览器窗口!

这个窗口显示的样子在不同的操作系统上会有所不同。我选择使用 Windows 的经典样式。非常赞!

Quick-Start Electron 应用的主窗口

正如我之前所说,你可以在应用中使用 Chrome 开发者工具,这个工具的用法跟在浏览器中的开发者工具一样,再赞一个!

Electron 应用程序架构

我们来看看这个应用的源代码及其文件结构。可以使用你自己喜欢的编辑器或者 IDE 打开项目,我使用 Atom …… 你猜到了 …… 它就是用 Electron 构建的![译者注:我比较喜欢 VSCode,也是基于 Electron 构建的]

新应用的目录和文件结构。

我们有一个基本的文件结构:

electron-quick-start

- index.html
- main.js
- package.json
- render.js

文件结构类似于我们创建网页的结构。

我们有:

  • index.html 是一个 HTML5 页面,它具有很重要的作用:提供画布

  • main.js 创建窗口并处理系统事件

  • package.json 是应用的启动脚本。它包含了应用的信息,在主进行中运行

  • render.js 处理应用的渲染进程

也许你对主进程和渲染进程存有疑问。它们到底是什么,用来干什么?

很高兴你有此疑问。注意了,如果你来自浏览器的 JavaScript 领域,这对你来说可能是一块新的领域!

什么是进程?

看到“进程”这个词的时候,想像一下操作系统级的进程。那是运行在系统中的计算机程序实例。

启动 Electron 应用之后,查看 Windows 的任务管理器或者 macOS 的活动监视器,就可以看到与这个应用相关的进程。

这些进程都是并行运行的,为每个进程分配的内存和资源相互隔离。

如果我想创建一个 for 循环在渲染进程中逐步处理一些事件。

var a = 1;
for ( a = 1; a < 10; a ++) {
 console.log('This is a for loop');
}

这些改变只在渲染进程中有效,根本不会对主进行造成影响。“This is a for loop”消息只会出现在渲染模块中。

主进程

主进程控制着应用的生命周期。它内置了完整的 Node.js API,可以打开对话框,创建渲染进程,还可以处理其它其它与操作系统的交互操作,包括启动和退出应用。

按照惯例,这个进程写在名为 main.js 的文件中。不过你想使用其它名字也没有问题。

你可以在 package.json 文件中配置主进程文件的名称。

试验一下,打开 package.json 并将

“main”: “main.js”,

修改为

“main”: “mainTest.js”,

启动应用看看它是否仍然正常运行。

注意,主进程只有一个。

渲染进程

应用中的渲染进程是一个浏览器窗口。与主进程不同,可以存在多个独立的渲染进程。

因为渲染进程是各自独立的,如果其中一个崩溃了并不会影响到其它进程,这得益于 Chrominum 的多进程架构。

这些浏览器窗口就像演示网页一样,也可以被隐藏或自定义。

不过 Electron 内置了完整的 Node.js API,也就是说我们可以打开对话框或进行其它与操作系统的交互。

这样考虑;

[来源: Kristian Poslek]

还有一个问题,它们能以某种方式联系起来吗?

这些进程都在独立运行,但他们仍然需要通信,因为它们负责不同的任务,这尤其需要通信。

为此,存在一个进程间的通信系统或者 IPC。你可以使用 IPC 在主进程和渲染进程间进行通信。对于这个知识点更深入一些的解释,请阅读 Christian Engvall 的文章

上面说的都是开发 Electron 应用的基础知识。

现在回到我们的代码!

私有化

让我们给应用所在的目录起一个合适的名称。

将目录名从 electron-quick-start 改为 hear-me-type-tutorial。

重新在编辑器或 IDE 中打开这个目录,我们打开 package.json 来进一步定制应用标识。

package.json 包含了至关重要的应用信息。这里定义应用的名称、版本、主文件、作者、许可协议等。

现在把作者改成自己的名称,自豪感油然而生。

找到 “author” 参数,然后将值改成自己的名称。它看起来像这样:

“author”: “Carol Pelu”,

我们还要改其它一些参数。在 package.json 中找到 name(名称) 和 description(说明)并修改它们:

帅呆了!现在应用有了新名称,还有简短而清晰的说明。

记住,你可以在终端运行 npm start 来运行应用,以观察所做的改变。

我们会继续在应用中添加一些期望的功能。我们想在按下每个键的时候播放不同的声音。

哦,有趣的功能!

没有功能的应用是什么?什么都不是……

现在我们要给它添加功能。

为了让应用响应输入,我们必须定义一个元素来捕捉事件,然后触发期望的动作。

为此,我们会创建一个具有特殊名称的若干 audio 元素,对应于按键。然后我们会使用一个 switch 语句来定位按下的键,播放与之关联的声音。

如果你现在觉得有点复杂,不要怕,我会指引你一步步进行。

下载这个压缩包,它包含了我们要使用的所有声音文件。很快就会用到!

我们会打开 index.html 文件,创建一个 <audio> 元素,在我们的应用中加入声音。

在 <body> 元素内部,创建一个 div 元素,将其 class 属性设置为 audio。

在刚刚创建的 div 元素,创建 <audio> 元素,将其 ID 命名为 “A”,source 属性设置为 “sounds/A.mp3”,preload 属性设置为 “auto”。

preload=”auto” 用于告诉应用在页面加载的时候就加载完整的声音文件。index.html 是应用的主文件,所有声音都会在应用启动的时候加载。

下面是代码:

<div class="audio">
<audio id="A" src="sounds/A.mp3" preload="auto"></audio>
</div>

你的 index.html 应该就像这样。

现在 <audio> 指向一个未知的文件。我们要创建一个名为 soudes 的目录,并将所有声音文件解压到这个目录中。

非常好!现在唯一缺少的是 JavaScriopt 代码。

创建一个叫 functions.js 的新文件,并在 index.html 中通过 require 引用它,这样应用运行的时候才会执行 JS 代码。

在示例的 require(./renderer.js') 下载添加这样一行:

require('./functions.js')

之后项目看起来是这样的:

不错!一切就绪,下面是见证奇迹的时刻

打开 functions.js 文件并将下面的代码添加到文件中。我稍后解释这段代码。

document.onkeydown = function(e{
    switch (e.keyCode) {
        case 65:
            document.getElementById('A').play();
            break;
        default:
            console.log("Key is not found!");
    }
};

代码现在是这样:

打开 Bash 或者终端窗口,确保当前是在项目目录下,运行 npm start 来启动应用。

调整扬声器的音量并敲下按键。

#MindBlown

JS 代码非常简单明了。

我们使用了 document 对象上的 onkeydown 事件,在这里找到被访问的元素。记住,document 对象是应用的主窗口。

我们在在匿名函数中使用了 switch 语句,根据 Unicode 值来判断按键。

如果找到按键对应的 Unicode 值,就会播放相应的声音,否则抛出 “not found” 错误。这个错误要在控制台中去找。

多么愉快的过程!

你可能注意到了,我们的声音文件包含了 A-Z 和 0-9 这些键,把它们都用起来。

在 index.html 中为每个有对应声音文件的键都创建一个 <audio> 元素。

之后代码就像这样:

当然你可以用拷贝粘贴:

<audio id="B" src="sounds/B.mp3" preload="auto"></audio>
<audio id="C" src="sounds/C.mp3" preload="auto"></audio>
<audio id="D" src="sounds/D.mp3" preload="auto"></audio>
<audio id="E" src="sounds/E.mp3" preload="auto"></audio>
<audio id="F" src="sounds/F.mp3" preload="auto"></audio>
<audio id="G" src="sounds/G.mp3" preload="auto"></audio>
<audio id="H" src="sounds/H.mp3" preload="auto"></audio>
<audio id="I" src="sounds/I.mp3" preload="auto"></audio>
<audio id="J" src="sounds/J.mp3" preload="auto"></audio>
<audio id="K" src="sounds/K.mp3" preload="auto"></audio>
<audio id="L" src="sounds/L.mp3" preload="auto"></audio>
<audio id="M" src="sounds/M.mp3" preload="auto"></audio>
<audio id="N" src="sounds/N.mp3" preload="auto"></audio>
<audio id="O" src="sounds/O.mp3" preload="auto"></audio>
<audio id="P" src="sounds/P.mp3" preload="auto"></audio>
<audio id="Q" src="sounds/Q.mp3" preload="auto"></audio>
<audio id="R" src="sounds/R.mp3" preload="auto"></audio>
<audio id="S" src="sounds/S.mp3" preload="auto"></audio>
<audio id="T" src="sounds/T.mp3" preload="auto"></audio>
<audio id="U" src="sounds/U.mp3" preload="auto"></audio>
<audio id="V" src="sounds/V.mp3" preload="auto"></audio>
<audio id="W" src="sounds/W.mp3" preload="auto"></audio>
<audio id="X" src="sounds/X.mp3" preload="auto"></audio>
<audio id="Y" src="sounds/Y.mp3" preload="auto"></audio>
<audio id="Z" src="sounds/Z.mp3" preload="auto"></audio>
<audio id="0" src="sounds/0.mp3" preload="auto"></audio>
<audio id="1" src="sounds/1.mp3" preload="auto"></audio>
<audio id="2" src="sounds/2.mp3" preload="auto"></audio>
<audio id="3" src="sounds/3.mp3" preload="auto"></audio>
<audio id="4" src="sounds/4.mp3" preload="auto"></audio>
<audio id="5" src="sounds/5.mp3" preload="auto"></audio>
<audio id="6" src="sounds/6.mp3" preload="auto"></audio>
<audio id="7" src="sounds/7.mp3" preload="auto"></audio>
<audio id="8" src="sounds/8.mp3" preload="auto"></audio>
<audio id="9" src="sounds/9.mp3" preload="auto"></audio>

现在在 functions.js 中加点代码。

你可以在这个网站上查到字符码(charCode 或 keyCode)。

当然,也可以使用拷贝粘贴:

document.onkeydown = function(e{
    switch (e.keyCode) {
        case 48:
            document.getElementById('0').play();
            break;
        case 49:
            document.getElementById('1').play();
            break;
        case 50:
            document.getElementById('2').play();
            break;
        case 51:
            document.getElementById('3').play();
            break;
        case 52:
            document.getElementById('4').play();
            break;
        case 53:
            document.getElementById('5').play();
            break;
        case 54:
            document.getElementById('6').play();
            break;
        case 55:
            document.getElementById('7').play();
            break;
        case 56:
            document.getElementById('8').play();
            break;
        case 57:
            document.getElementById('9').play();
            break;
        case 65:
            document.getElementById('A').play();
            break;
        case 66:
            document.getElementById('B').play();
            break;
        case 67:
            document.getElementById('C').play();
            break;
        case 68:
            document.getElementById('D').play();
            break;
        case 69:
            document.getElementById('E').play();
            break;
        case 70:
            document.getElementById('F').play();
            break;
        case 71:
            document.getElementById('G').play();
            break;
        case 72:
            document.getElementById('H').play();
            break;
        case 73:
            document.getElementById('I').play();
            break;
        case 74:
            document.getElementById('J').play();
            break;
        case 75:
            document.getElementById('K').play();
            break;
        case 76:
            document.getElementById('L').play();
            break;
        case 77:
            document.getElementById('M').play();
            break;
        case 78:
            document.getElementById('N').play();
            break;
        case 79:
            document.getElementById('O').play();
            break;
        case 80:
            document.getElementById('P').play();
            break;
        case 81:
            document.getElementById('Q').play();
            break;
        case 82:
            document.getElementById('R').play();
            break;
        case 83:
            document.getElementById('S').play();
            break;
        case 84:
            document.getElementById('T').play();
            break;
        case 85:
            document.getElementById('U').play();
            break;
        case 86:
            document.getElementById('V').play();
            break;
        case 87:
            document.getElementById('W').play();
            break;
        case 88:
            document.getElementById('X').play();
            break;
        case 89:
            document.getElementById('Y').play();
            break;
        case 90:
            document.getElementById('Z').play();
            break;
        default:
            console.log("Key is not found!");    
    }
};

大功告成!

应用的主要功能已经完成了,但仍然还有些工作要做!

完善!

应用程序的功能已经完成,但它尚不完善。

比如,可以在 index.html 中修应用的标题和主窗口的内容。

此外,这个应用没有设计炫丽的色彩,也没有使用漂亮的图片。

充分发挥你的想像,找出改进应用设计的方法。

代码也不完美,我们有很多相同的代码需要优化以减少代码行数,至少看起来不那么难受。

重复代码真不是好做法!

测试!就是测试!

好的软件需要通过测试。

我建议你先按键看看,会发生什么事。

最好的情况是你会听到每个键对应的声音。但如果你快速的按下多个键的时候会发生什么呢?如果按下了非预期的键,比如 Home 和 NumLock,又会发生什么呢?

如果你最小化程序再尝试着按钮会怎样?能听到声音吗?如果没有选择应用程序窗口,按下键盘时,还会听到声音吗?

答案是否定的。

Electron 的架构决定了其行为。它允许你可以像 C# 语言那样使用所有按键,但你不能注册特殊的按键。这已经超出了 Electron 应用的使用范围。

一行行的执行代码,并深度中断它,看看会发生什么,Electron 会抛出什么样的错误。这一练习能帮助你更好地进行调试。如果你知道应用的缺陷,那么你就知道该如何去修复,让应用变得更好。

我故意在 functions.js 文件中使用了一个废弃的 JavaScript 事件,你能发现吗?

如果你找到了,我希望你能在不改变应用程序功能的情况下替换它。

使用废弃的代码很不好,这可能会导致严重错误,你甚至可能意识不到这些错误的存在。关注语言的最新文件,了解可能发生的变化,始终了解最新状态。

结尾

非常感谢你的阅读。

你现在已经有创建一个简单的跨平台 Electron 应用的能力了。

如果你想更深入的了解 Electron,了解我对 Hear Me Type 的改进,还有我 GitHub 上的资料。

欢迎你来 clone, fork, star 我所有公开的项目,或者提交贡献。

希望你回过头来再次查看我这篇文章,我会持续更新这篇文章以适应 Electron 的版本。

感谢你花费宝贵时间来查看我的文章。

本文最先发布于 NeutronDev.com.

如果你想知道更多关于本文或者 Electron 的信息,欢迎来文章后面评论。

原文来自:开源中国

声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com

  • 购物小票识别

    支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景

    支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景

  • 涉农贷款地址识别

    涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。

    涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。

  • 人脸四要素

    根据给定的手机号、姓名、身份证、人像图片核验是否一致

    根据给定的手机号、姓名、身份证、人像图片核验是否一致

  • 个人/企业涉诉查询

    通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。

    通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。

  • IP反查域名

    IP反查域名是通过IP查询相关联的域名信息的功能,它提供IP地址历史上绑定过的域名信息。

    IP反查域名是通过IP查询相关联的域名信息的功能,它提供IP地址历史上绑定过的域名信息。

0512-88869195
数 据 驱 动 未 来
Data Drives The Future