第二章 你第首个Electron应用 | Electron in Action(中译)

本章主要内容

  • 构建并启动Electron应用

  • 生成package.json,配置成Electron应用

  • 在你的项目中包含预先构建Electron版本

  • 配置package.json以启动主进程

  • 从主进程生成渲染进程

  • 利用Electron限制宽松的优点构建通常在浏览器无法构建的功能

  • 使用Electron的内置模块来回避一些常见的问题

在第一章中,我们从高的层次上,讨论了什么是Electron。说到底这本书叫做《Electron实战》,对吧?在本章中,我们通过从头开始设置和构建一个简单的应用程序来管理书签列表,从而学习Electron的基本知识。该应用程序将利用只有在现代的浏览器中才能使用的特性。

在上一章的高层次讨论中,我提到了Electron是一个类似于Node的运行时。这仍然是正确的,但是我想回顾下这一点。Electron不是一个框架——它不提供任何框架,也没有关于如何构造应用程序或命名文件的严格规则,这些选择都留给了我们这些开发者。好的一面是,它也不强制执行任何约定,而且在入手之前,我们不需要多少概念上的样板信息去学习。

 

构建书签列表应用程序

 

让我们从构建一个简单而又有些幼稚的Electron应用程序开始,来加强我们已经介绍过的所有内容的理解。我们的应用程序接受url。当用户提供URL时,我们获取URL引用的页面的标题,并将其保存在应用程序的localStorage中。最后,显示应用程序中的所有链接。您可以在GitHub上找到本章的完整源代码(https://github.com/electron-in-action/bookmarker)。

  在此过程中,我们将指出构建Electron应用程序的一些优点,例如,可以绕过对服务器的需求,使用最前沿的web api,这些web api并不广泛支持所有浏览器,因为这些APIs是在现代版本的Chromium中实现。图2.1是我们在本章构建的应用程序的效果图。

第二章 你第首个Electron应用 | Electron in Action(中译)_第1张图片

图2.1 我们在本章中构建的应用程序效果图

 

  当用户希望将网站URL保存并添加到输入字段下面的列表中时,应用程序向网站发送一个请求来获取标记。成功接收到标记后,应用程序获取网站的标题,并将标题和URL添加到网站列表中,该列表存储在浏览器的localStorage中。当应用程序启动时,它从localStorage读取并恢复列表。我们添加了一个带有命令的按钮来清除localStorage,以防出现错误。因为这个简单的应用程序旨在帮助您熟悉Electron,所以我们不会执行高级操作,比如从列表中删除单个网站。

 

搭建Electron应用

  1. npm init 生成package.json

    第二章 你第首个Electron应用 | Electron in Action(中译)_第2张图片

  2. 搭建Electron目录框架

    第二章 你第首个Electron应用 | Electron in Action(中译)_第3张图片

 

应用程序结构的定义取决于您的团队或个人处理应用程序的方式。许多开发人员采用的方法略有不同。观察学习一些更成熟的电子应用程序,我们可以辨别出共同的模式,并在本书中决定如何处理我们的应用程序。

出于我们的目的,为了让本书文件结构达成一致。做出一下规定,我们有一个应用程序目录,其中存储了所有的应用程序代码。我们还有一个package.json将存储依赖项列表、关于应用程序的元数据和脚本,并声明Electron应该在何处查找主进程。在安装了依赖项之后,最终会得到一个由Electron为我们创建的node_modules目录,但是我们不会在初始设置中包含它

就文件而言,让我们从应用程序中的两个文件开始:main.jsrenderer.js。它们是带有标识的文件名,因此我们可以跟踪这两种类型的进程。我们在本书中构建的所有应用程序的开始大致遵循图2.2中所示的目录结构。(如果你在运行macOS,你可以通过安装brew install tree使用tree命令。)

第二章 你第首个Electron应用 | Electron in Action(中译)_第4张图片

图2.2 我们第一个Electron应用的文件结构树

 

创建一个名为“bookmarker”的目录,并进入此目录。您可以通过从命令行工具运行以下两个命令来快速创建这个结构。当你使用npm init之后,你会生成一个package.json文件。

mkdir app
touch app/main.js app/renderer.js app/style.css app/index.html

 

Electron本身不需要这种结构,但它受到了其他Electron应用程序建立的一些最佳实践的启发。Atom将所有应用程序代码保存在一个app目录中,将所有样式表和其他资产(如图像)保存在一个静态目录中。LevelUI在顶层有一个index.js和一个client.js,并将所有依赖文件保存在src目录中,样式表保存在styles目录中。Yoda将所有文件(包括加载应用程序其余部分的文件)保存在src目录中。app、src和lib是存放应用程序大部分代码的文件夹的常用名称,style、static和assets是存放应用程序中使用的静态资产的目录的常用名称。

 

package.json

package.json清单用于许多甚至说大多数Node项目。此清单包含有关项目的重要信息。它列出了元数据,比如作者的姓名以及他们的电子邮件地址、项目是在哪个许可下发布的、项目的git存储库的位置以及文件问题的位置。它还为一些常见的任务定义了脚本,比如运行测试套件或者与我们的需求相关的构建应用程序。package.json文件还列出了用于运行和开发应用程序的所有依赖项。

理论上,您可能有一个没有package.json的Node项目。但是,当加载或构建应用程序时,Electron依赖于该文件及其主要属性来确定从何处开始。

npm是Node附带的包管理器,它提供了一个有用的工具帮助生成package.json。在前面创建的“bookmarker”目录中运行npm init。如果您将提示符留空,npm将冒号后面括号中的内容作为默认内容。您的内容应该类似于图2.3,当然,除了作者的名字之外。

在package.json中,值得注意的是main条目。这里,你可以看到我将它设置为"./app/main.js"。基于我们如何设置应用程序。你可以指向任何你想要的文件。我们要用的主文件恰好叫做main.js。但是它可以被命名为任何东西(例如,sandwich.js、index.js、app.js)。

第二章 你第首个Electron应用 | Electron in Action(中译)_第5张图片

图2.3 npm init 提供一系列提示并设置一个package.json文件

 

下载和安装Electron在我们的项目

我们已经建立了应用程序的基本结构,但是却找不到Electron。从源代码编译Electron需要一段时间,而且可能很乏味。因此我们根据每个平台(macOS、Windows和Linux)以及两种体系结构(32位和64位)预先构建了electronic版本。我们通过npm安装Electron。

下载和安装电子很容易。在您运行npm init之前,在你的项目目录中运行以下命令:

npm install electron --save-dev

此命令将在你的项目node_modules目录下下载并安装Electron(如果您还没有目录,它还会创建目录)。--save-dev标志将其添加到package.json的依赖项列表中。这意味着如果有人下载了这个项目并运行npm install,他们将默认获得Electron。

 

漫谈electron-prebuilt

假如您了解Electron的历史,您可能会看到博客文章、文档,甚至本书的早期版本,其中提到的是electron-prebuilt,而不是electron。在过去,前者是为操作系统安装预编译版Electron的首选方法。后者是新的首选方法。从2017年初开始,不再支持electron-prebuilt

 

npm还允许您定义在package.json中运行公共脚本的快捷方式。当您运行package.json定义的脚本时。npm自动添加node_modules到这个路径。这意味着它将默认使用本地安装的Electron版本。让我们向package.json添加一个start脚本。

列表2.1  向package.json添加一个启动脚本

{                                                        +
"name": "bookmarker",                                    |当我们运行npm start
"version": "1.0.0",                                      |npm将会运行什么脚本
"description": "Our very first Electron application",    |
"main": "./app/main.js",                                 |
"scripts": {                                             |
"start": "electron .",                            <------+
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Steve Kinney",
"license": "ISC",
"dependencies": {
"electron": "^2.0.4"
}
}

现在,当我们运行npm start时,npm使用我们本地安装的版本Electron去启动Electron应用程序。你会注意到似乎没有什么事情发生。在你的终端中,它实际运行以下程式码:

>[email protected]  start /Users/stevekinney/Projects/bookmarker
>electron .

您还将在dock或任务栏中看到一个新应用程序(我们刚刚设置的Electron应用程序),如图2.4所示。它被简称为“Electron”,并使用Electron的默认应用程序图标。在后面的章节中,我们将看到如何定制这些属性,但是目前默认值已经足够好了。我们所有的代码文件都是完全空白的。因此,这个应用程序还有很多操作需要去做,但是它确实存在并正确启动。我们认为这是一场暂时的胜利。在windows上关闭应用程序的所有窗口或选择退出应用程序菜单终止进程。或者,您可以在Windows命令提示符或终端中按Control-C退出应用程序。按下Command-Period将终止macOS上的进程。

第二章 你第首个Electron应用 | Electron in Action(中译)_第6张图片

图2.4 dock上的应用程序就是我们刚建立的电子应用

处理主进程

现在我们有了一个Electron应用,如果我们真的能让它做点什么,那就太好了。如果你还记得第一章,我们从可以创建一个或多个渲染器进程的主进程开始。我们首先通过编写main.js代码,迈出我们应用程序的第一步。

要处理Electron,我们需要导入electron库。Electron附带了许多有用的模块,我们在本书中使用了这些模块。第一个—也可以说是最重要的——是app模块。

列表2.2 添加一个基本的主进程: ./app/main.js


const {app} = require('electron');     +
app.on('ready', () => {            <---+ 在应用程序完全
console.log('Hello from Electron');   + 启后立即调用
});

app是一个处理应用程序生命周期和配置的模块。我们可以使用它退出、隐藏和显示应用程序,以及获取和设置应用程序的属性。app模块还可以运行事件-包括before-quit, window -all-closed,

browser-window-blur, 和browser-window-focus-当应用程序进入不同状态时。

在应用程序完全启动并准备就绪之前,我们无法处理它。幸运的是,app触发了一个ready事件。这意味着在做任何事之前,我们需要耐心等待并监听应用程序启动ready事件。在前面的代码中,我们在控制台打印日志,这是一件无需Electron就可以轻松完成的事情,但是这段代码强调了如何侦听ready事件。

 

创建渲染器进程

我们的主进程与其他Node进程非常相似。它可以访问Node的所有内置库以及由Electron提供的一组特殊模块,我们将在本书中对此进行探讨。但是,与任何其他Node进程一样,我们的主进程没有DOM(文档对象模型),也不能呈现UI。主进程负责与操作系统交互,管理状态,并与应用程序中的所有其他流程进行协调。它不负责呈现HTML和CSS。这就是渲染器进程的工作。参与整个Electron主要功能之一是为Node进程创建一个GUI。

主进程可以使用BrowserWindow创建多个渲染器进程。每个BrowserWindow都是一个单独的、惟一的渲染器器进程,包括一个DOM,访问Chromium web APIs,以及Node内置模块。访问BrowserWindow模块的方式与访问app模块的方式相同。

 

列表2.3 引用BrowserWindow模块: ./app/main.js

const {app, BrowserWindow} = require('electron');

 

您可能已经注意到BrowserWindow模块以大写字母开头。根据标准JavaScript约定,这通常意味着我们用new关键字将其调用为构造函数。我们可以使用这个构造函数创建尽可能多的渲染器进程,只要我们喜欢,或者我们的计算机可以处理。当应用程序就绪时,我们创建一个BrowserWindow实例。让我们按照以下方式更新代码。

 

列表2.4 生成一个BrowserWindow: ./app/main.js

                                                    +
const {app, BrowserWindow} = require('electron');   |在我们的应用程序中创建一个
let mainWindow = null;                         <----+window对象的全局引用
app.on('ready', () => {                  +          +
console.log('Hello from Electron.');    |当应用程序准备好时,
mainWindow = new BrowserWindow();  <----+创建一个浏览器窗口
});                                      +并将其分配给全局变量

我们在ready事件监听器外声明了mainWindow。JavaScript使用函数作用域。如果我们在事件监听器中声明mainWindow, mainWindow将进行垃圾回收,因为分配给ready事件的函数已经运行完毕。如果被垃圾回收,我们的窗户就会神秘地消失。如果我们运行这段代码,我们会在屏幕中央看到一个不起眼的小窗口,如图2.5所示。

第二章 你第首个Electron应用 | Electron in Action(中译)_第7张图片

一个没有加载HTML文档的空BrowserWindow

 

这是一扇窗口,并什么好看的。下一步是将HTML页面加载到我们创建的BrowserWindow实例中。所有BrowserWindow实例都有一个web content属性,该属性具有几个有用的特性,比如将HTML文件加载到渲染器进程的窗口中、从主进程向渲染器进程发送消息、将页面打印为PDF或打印机等等。现在,我们最关心的是将内容加载到我们刚刚创建的那个无聊的窗口中。

  我们需要加载一个HTML页面,因此在您项目的app目录中创建index.html。让我们将以下内容添加到HTML页面,使其成为一个有效的文档。

 

列表2.5 创建index.html: ./app/index.html

DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self' 'unsafe-inline';
connect-src *
"
>
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Bookmarker</title>
</head>
<body>
<h1>Hello from Electron</h1>
</body>
</html>

这很简单,但它完成了工作,并为构建打下了良好的基础。我们将以下代码添加到app/main.js中,以告诉渲染器进程在我们之前创建的窗口中加载这个HTML文档。

列表2.6 将HTML文档加载到主窗口: ./app/main.js

我们使用file://protocol_dirname变量,该变量在Node中全局可用。_dirname是Node进程正在执行的目录的完整路径。在我的例子中,_dirname扩展为/Users/stevekinney/Projects/bookmarker/app

现在,我们可以使用npm start启动应用程序,并观察它加载新的HTML文件。如果一切顺利,您应该会看到类似于图2.6的内容。

 

从渲染进程加载代码

从渲染器进程加载的HTML文件中,我们可以像在传统的基于浏览器的web应用程序中一样加载可能需要的任何其他文件-即

你可能感兴趣的:(第二章 你第首个Electron应用 | Electron in Action(中译))