电子 - 快速指南


电子 - 概述

为什么是电子?

Electron 提供具有丰富本机(操作系统)API 的运行时,使您能够使用纯 JavaScript 创建桌面应用程序。

这并不意味着 Electron 是绑定到图形用户界面 (GUI) 库的 JavaScript。相反,Electron 使用网页作为其 GUI,因此您也可以将其视为由 JavaScript 控制的最小 Chromium 浏览器。因此,从技术上讲,所有电子应用程序都是在浏览器中运行的网页,可以利用操作系统 API。

谁使用电子?

Github 开发了 Electron 来创建文本编辑器 Atom。它们均于 2014 年开源。Electron 被许多公司使用,例如 Microsoft、Github、Slack 等。

Electron 已被用来创建许多应用程序。以下是一些著名的应用程序 -

  • 松弛桌面
  • WordPress 桌面应用程序
  • 视觉工作室代码
  • 插入符号 Markdown 编辑器
  • 奈拉斯电子邮件应用程序
  • GitKraken git 客户端

电子 - 安装

要开始使用 Electron 进行开发,您需要安装 Node 和 npm(节点包管理器)。如果您还没有这些,请转到节点设置以在本地系统上安装节点。通过在终端中运行以下命令来确认已安装 Node 和 npm。

node --version
npm --version

上面的命令将生成以下输出 -

v6.9.1
3.10.8

每当我们使用 npm 创建项目时,我们都需要提供一个package.json文件,其中包含有关我们项目的所有详细信息。npm 使我们可以轻松设置此文件。让我们建立我们的开发项目。

  • 启动终端/cmd,创建一个名为 hello-world 的新文件夹,然后使用 cd 命令打开该文件夹。

  • 现在要使用 npm 创建 package.json 文件,请使用以下命令。

npm init
  • 它会要求您提供以下信息 -

Package.json 创建

只需按住 Enter 键,然后在“作者姓名”字段中输入您的姓名即可。

创建一个新文件夹并使用 cd 命令将其打开。现在运行以下命令来全局安装 Electron。

$ npm install -g electron-prebuilt

一旦执行,您可以通过运行以下命令来检查 Electron 是否安装正确:

$ electron --version

你应该得到输出 -

v1.4.13

现在我们已经设置了 Electron,让我们继续使用它创建我们的第一个应用程序。

电子如何工作

Electron 获取package.json文件中定义的主文件并执行它。该主文件创建应用程序窗口,其中包含渲染的网页以及与操作系统的本机 GUI(图形用户界面)的交互。

当您使用 Electron 启动应用程序时,会创建一个主进程。该主进程负责与操作系统的本机 GUI 进行交互。它创建您的应用程序的 GUI。

仅启动主进程不会为应用程序的用户提供任何应用程序窗口。它们是由主进程使用BrowserWindow模块在主文件中创建的。然后,每个浏览器窗口都运行自己的渲染器进程。渲染器进程采用一个 HTML 文件,该文件引用常用的 CSS 文件、JavaScript 文件、图像等,并将其渲染在窗口中。

主进程可以通过 Electron 中直接可用的模块访问本机 GUI。桌面应用程序可以访问所有 Node 模块,例如用于处理文件的文件系统模块、请求进行 HTTP 调用等。

主进程和渲染进程之间的区别

主进程通过创建BrowserWindow实例来创建网页。每个BrowserWindow实例在其自己的渲染器进程中运行网页。当一个BrowserWindow实例被销毁时,相应的渲染器进程也会被终止。

主进程管理所有网页及其相应的渲染器进程。每个渲染器进程都是隔离的,只关心其中运行的网页。

电子 - 你好世界

我们为我们的项目创建了一个package.json文件。现在我们将使用 Electron 创建我们的第一个桌面应用程序。

创建一个名为main.js的新文件。在其中输入以下代码 -

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

let win  

function createWindow() { 
   win = new BrowserWindow({width: 800, height: 600}) 
   win.loadURL(url.format ({ 
      pathname: path.join(__dirname, 'index.html'), 
      protocol: 'file:', 
      slashes: true 
   })) 
}  

app.on('ready', createWindow) 

创建另一个文件,这次是一个名为index.html的 HTML 文件。在其中输入以下代码。

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Hello World!</title>
   </head>
   
   <body>
      <h1>Hello World!</h1>
      We are using node <script>document.write(process.versions.node)</script>,
      Chrome <script>document.write(process.versions.chrome)</script>,
      and Electron <script>document.write(process.versions.electron)</script>.
   </body>
</html>

使用以下命令运行此应用程序 -

$ electron ./main.js

将打开一个新窗口。它将如下所示 -

电子你好世界

这个应用程序如何工作?

我们创建了一个主文件和一个 HTML 文件。主文件使用两个模块 - appBrowserWindow。app 模块用于控制应用程序的事件生命周期,而 BrowserWindow 模块用于创建和控制浏览器窗口。

我们定义了一个createWindow函数,在其中创建一个新的 BrowserWindow 并将 URL 附加到该 BrowserWindow。这是当我们运行应用程序时呈现并显示给我们的 HTML 文件。

我们在 html 文件中使用了原生 Electron 对象进程。该对象是从 Node.js 进程对象扩展而来的,包含所有t=its功能,同时添加了更多功能。

Electron - 构建 UI

Electron 应用程序的用户界面是使用 HTML、CSS 和 JS 构建的。因此,我们也可以在这里利用所有可用的前端 Web 开发工具。您可以使用 Angular、Backbone、React、Bootstrap 和 Foundation 等工具来构建应用程序。

您可以使用 Bower 来管理这些前端依赖项。使用安装凉亭 -

$ npm install -g bower

现在您可以使用 Bower 获取所有可用的 JS 和 CSS 框架、库、插件等。例如,要获取 bootstrap 的最新稳定版本,请输入以下命令 -

$ bower install bootstrap

这将在Bower_components中下载 bootstrap 。现在您可以在 HTML 中引用该库。让我们使用这些库创建一个简单的页面。

现在让我们使用 npm 命令安装 jquery -

$ npm install --save jquery

此外,我们的 view.js 文件中也需要这样做。我们已经有一个 main.js 设置如下 -

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

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

打开您的index.html文件并在其中输入以下代码 -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Hello World!</title>
      <link rel = "stylesheet" 
         href = "./bower_components/bootstrap/dist/css/bootstrap.min.css" />
   </head>
   
   <body>
      <div class = "container">
         <h1>This page is using Bootstrap and jQuery!</h1>
         <h3 id = "click-counter"></h3>
         <button class = "btn btn-success" id = "countbtn">Click here</button>
         <script src = "./view.js" ></script>
      </div>
   </body>
</html>

创建view.js并在其中输入点击计数器逻辑 -

let $ = require('jquery')  // jQuery now loaded and assigned to $
let count = 0
$('#click-counter').text(count.toString())
$('#countbtn').on('click', () => {
   count ++ 
   $('#click-counter').text(count)
}) 

使用以下命令运行应用程序 -

$ electron ./main.js

上面的命令将生成如下屏幕截图所示的输出 -

用户界面

您可以像构建网站一样构建本机应用程序。如果您不希望用户被限制在确切的窗口大小,您可以利用响应式设计并允许用户以灵活的方式使用您的应用程序。

Electron - 文件处理

文件处理是构建桌面应用程序的一个非常重要的部分。几乎所有桌面应用程序都与文件交互。

我们将在应用程序中创建一个表单,将名称和电子邮件地址作为输入。该表单将被保存到一个文件中,并且将创建一个列表,将其显示为输出。

使用main.js文件中的以下代码设置主进程-

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

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

现在打开index.html文件并在其中输入以下代码 -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>File System</title>
      <link rel = "stylesheet" 
         href = "./bower_components/bootstrap/dist/css/bootstrap.min.css" />
      
      <style type = "text/css">
         #contact-list {
            height: 150px;
            overflow-y: auto;
         }
      </style>
   </head>
   
   <body>
      <div class = "container">
         <h1>Enter Names and Email addresses of your contacts</h1>
         <div class = "form-group">
            <label for = "Name">Name</label>
            <input type = "text" name = "Name" value = "" id = "Name" 
               placeholder = "Name" class = "form-control" required>
         </div>
         
         <div class = "form-group">
            <label for = "Email">Email</label>
            <input type = "email" name = "Email" value = "" id = "Email" 
               placeholder = "Email" class = "form-control" required>
         </div>
         
         <div class = "form-group">
            <button class = "btn btn-primary" id = "add-to-list">Add to list!</button>
         </div>
         
         <div id = "contact-list">
            <table class = "table-striped" id = "contact-table">
               <tr>
                  <th class = "col-xs-2">S. No.</th>
                  <th class = "col-xs-4">Name</th>
                  <th class = "col-xs-6">Email</th>
               </tr>
            </table>
         </div>
         
         <script src = "./view.js" ></script>
      </div>
   </body>
</html>

现在我们需要处理添加事件。我们将在view.js文件中执行此操作。

我们将创建一个函数loadAndDisplayContacts(),该函数最初将从文件中加载联系人。创建loadAndDisplayContacts()函数后,我们将在“添加到列表”按钮上创建一个单击处理程序。这会将条目添加到文件和表中。

在 view.js 文件中,输入以下代码 -

let $ = require('jquery')
let fs = require('fs')
let filename = 'contacts'
let sno = 0

$('#add-to-list').on('click', () => {
   let name = $('#Name').val()
   let email = $('#Email').val()

   fs.appendFile('contacts', name + ',' + email + '\n')

   addEntry(name, email)
})

function addEntry(name, email) {
   if(name && email) {
      sno++
      let updateString = '<tr><td>'+ sno + '</td><td>'+ name +'</td><td>' 
         + email +'</td></tr>'
      $('#contact-table').append(updateString)
   }
}

function loadAndDisplayContacts() {  
   
   //Check if file exists
   if(fs.existsSync(filename)) {
      let data = fs.readFileSync(filename, 'utf8').split('\n')
      
      data.forEach((contact, index) => {
         let [ name, email ] = contact.split(',')
         addEntry(name, email)
      })
   
   } else {
      console.log("File Doesn\'t Exist. Creating new file.")
      fs.writeFile(filename, '', (err) => {
         if(err)
            console.log(err)
      })
   }
}

loadAndDisplayContacts()

现在使用以下命令运行应用程序 -

$ electron ./main.js

一旦您添加一些联系人,应用程序将如下所示 -

文件

更多fs模块API调用请参考节点文件系统教程

现在我们可以使用 Electron 处理文件。我们将在对话框章节中了解如何调用文件的保存和打开对话框(本机)。

Electron - 原生节点库

我们在上一章中使用了节点模块 fs。现在我们将了解一些可以与 Electron 一起使用的其他节点模块。

操作系统模块

使用 OS 模块,我们可以获得有关应用程序运行的系统的大量信息。以下是在创建应用程序时提供帮助的一些方法。这些方法帮助我们根据应用程序运行的操作系统来自定义应用程序。

先生编号 功能说明
1

os.userInfo([选项])

os.userInfo ()方法返回有关当前有效用户的信息。即使没有明确询问信息,该信息也可用于为用户个性化应用程序。

2

os.platform()

os.platform ()方法返回一个标识操作系统平台的字符串。这可用于根据用户操作系统自定义应用程序。

3

os.homedir()

os.homedir ()方法以字符串形式返回当前用户的主目录。一般来说,所有用户的配置都位于用户的主目录中。因此,这可以用于我们的应用程序的相同目的。

4

os.arch()

os.arch ()方法返回一个标识操作系统 CPU 架构的字符串。当在奇异的体系结构上运行时可以使用它来调整您的应用程序以适应该系统。

5

停产

定义特定于操作系统的行结束标记的字符串常量。每当主机操作系统上的文件中结束行时,都应该使用此选项。

使用相同的 main.js 文件和以下 HTML 文件,我们可以在屏幕上打印这些属性 -

<html>
   <head>
      <title>OS Module</title>
   </head>
   
   <body>
      <script>
         let os = require('os')
         document.write('User Info: ' + JSON.stringify(os.userInfo()) + '<br>' + 
            'Platform: ' + os.platform() + '<br>' + 
            'User home directory: ' +  os.homedir() + '<br>' + 
            'OS Architecture: ' + os.arch() + '<br>')
      </script>
   </body>
</html>

现在使用以下命令运行应用程序 -

$ electron ./main.js

上面的命令将生成以下输出 -

User Info: {"uid":1000,"gid":1000,"username":"ayushgp","homedir":"/home/ayushgp",
   "shell":"/usr/bin/zsh"}
Platform: linux
User home directory: /home/ayushgp
OS Architecture: x64

网络模块

net 模块用于应用程序中与网络相关的工作。我们可以使用此模块创建服务器和套接字连接。一般来说,对于网络相关任务,建议使用 npm 中的包装器模块,而不是使用 net 模块。

下表列出了该模块中最有用的方法 -

先生编号 功能说明
1

net.createServer([选项][, 连接监听器])

创建一个新的 TCP 服务器。ConnectionListener 参数自动设置为“connection”事件的侦听器。

2

net.createConnection(选项[,connectionListener])

一个工厂方法,返回一个新的“net.Socket”并连接到提供的地址和端口。

3

net.Server.listen(端口[, 主机][, 积压][, 回调])

开始接受指定端口和主机上的连接。如果省略主机,服务器将接受指向任何 IPv4 地址的连接。

4

net.Server.close([回调])

当所有连接都结束并且服务器发出“关闭”事件时最终关闭。

5

net.Socket.connect(端口[, 主机][, connectListener])

打开给定套接字的连接。如果指定了端口和主机,则套接字将作为 TCP 套接字打开。

net 模块还附带了一些其他方法。要获得更全面的列表,请参阅

现在,让我们创建一个使用 net 模块创建与服务器的连接的电子应用程序。我们需要创建一个新文件server.js -

var net = require('net');
var server = net.createServer(function(connection) { 
   console.log('Client Connected');
   
   connection.on('end', function() {
      console.log('client disconnected');
   });
   
   connection.write('Hello World!\r\n');
   connection.pipe(connection);
});

server.listen(8080, function() { 
   console.log('Server running on http://localhost:8080');
});

使用相同的 main.js 文件,将 HTML 文件替换为以下内容 -

<html>
   <head>
      <title>net Module</title>
   </head>
   
   <body>
      <script>
         var net = require('net');
         var client = net.connect({port: 8080}, function() {
            console.log('Connection established!');  
         });
         
         client.on('data', function(data) {
            document.write(data.toString());
            client.end();
         });
         
         client.on('end', function() { 
            console.log('Disconnected :(');
         });
      </script>
   </body>
</html>

使用以下命令运行服务器 -

$ node server.js

使用以下命令运行应用程序 -

$ electron ./main.js

上面的命令将生成以下输出 -

网络模块

观察我们自动连接到服务器并自动断开连接。

我们还有一些其他节点模块,可以使用 Electron 直接在前端使用。这些模块的使用取决于您使用它们的场景。

电子进程间通信

Electron 为我们提供了 2 个 IPC(进程间通信)模块,分别称为ipcMainipcRenderer

ipcMain模块用于从主进程到渲染器进程进行异步通信。当在主进程中使用时,该模块处理从渲染器进程(网页)发送的异步和同步消息。从渲染器发送的消息将被发送到该模块。

ipcRenderer模块用于从渲染器进程到主进程进行异步通信。它提供了一些方法,以便您可以将同步和异步消息从渲染器进程(网页)发送到主进程。您还可以接收来自主进程的回复。

我们将创建一个主进程和一个渲染器进程,它们将使用上述模块相互发送消息。

创建一个名为main_process.js的新文件,其中包含以下内容 -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')
const {ipcMain} = require('electron')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

// Event handler for asynchronous incoming messages
ipcMain.on('asynchronous-message', (event, arg) => {
   console.log(arg)

   // Event emitter for sending asynchronous messages
   event.sender.send('asynchronous-reply', 'async pong')
})

// Event handler for synchronous incoming messages
ipcMain.on('synchronous-message', (event, arg) => {
   console.log(arg) 

   // Synchronous event emmision
   event.returnValue = 'sync pong'
})

app.on('ready', createWindow)

现在创建一个新的index.html文件并在其中添加以下代码。

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Hello World!</title>
   </head>
   
   <body>
      <script>
         const {ipcRenderer} = require('electron')

         // Synchronous message emmiter and handler
         console.log(ipcRenderer.sendSync('synchronous-message', 'sync ping')) 

         // Async message handler
         ipcRenderer.on('asynchronous-reply', (event, arg) => {
            console.log(arg)
         })

         // Async message sender
         ipcRenderer.send('asynchronous-message', 'async ping')
      </script>
   </body>
</html>

使用以下命令运行应用程序 -

$ electron ./main_process.js

上面的命令将生成以下输出 -

// On your app console
Sync Pong
Async Pong

// On your terminal where you ran the app
Sync Ping
Async Ping

建议不要在渲染器进程上执行繁重/阻塞任务的计算。始终使用 IPC 将这些任务委托给主进程。这有助于保持应用程序的进度。

Electron - 系统对话框

对于任何应用程序来说,用户友好是非常重要的。因此,您不应该使用alert() 调用创建对话框。Electron 提供了一个非常好的界面来完成创建对话框的任务。让我们来看看吧。

Electron 提供了一个对话框模块,我们可以使用它来显示本机系统对话框,以打开和保存文件、发出警报等。

让我们直接跳到一个示例并创建一个应用程序来显示简单的文本文件。

创建一个新的 main.js 文件并在其中输入以下代码 -

const {app, BrowserWindow} = require('electron') 
const url = require('url') 
const path = require('path') 
const {ipcMain} = require('electron')  

let win  

function createWindow() { 
   win = new BrowserWindow({width: 800, height: 600}) 
   win.loadURL(url.format ({ 
      pathname: path.join(__dirname, 'index.html'), 
      protocol: 'file:', 
      slashes: true 
   })) 
}  

ipcMain.on('openFile', (event, path) => { 
   const {dialog} = require('electron') 
   const fs = require('fs') 
   dialog.showOpenDialog(function (fileNames) { 
      
      // fileNames is an array that contains all the selected 
      if(fileNames === undefined) { 
         console.log("No file selected"); 
      
      } else { 
         readFile(fileNames[0]); 
      } 
   });
   
   function readFile(filepath) { 
      fs.readFile(filepath, 'utf-8', (err, data) => { 
         
         if(err){ 
            alert("An error ocurred reading the file :" + err.message) 
            return 
         } 
         
         // handle the file content 
         event.sender.send('fileData', data) 
      }) 
   } 
})  
app.on('ready', createWindow)

每当我们的主进程从渲染器进程收到“openFile”消息时,此代码就会弹出打开对话框。此消息会将文件内容重定向回渲染器进程。现在,我们必须打印内容。

现在,创建一个包含以下内容的新index.html文件 -

<!DOCTYPE html> 
<html> 
   <head> 
      <meta charset = "UTF-8"> 
      <title>File read using system dialogs</title> 
   </head> 
   
   <body> 
      <script type = "text/javascript"> 
         const {ipcRenderer} = require('electron') 
         ipcRenderer.send('openFile', () => { 
            console.log("Event sent."); 
         }) 
         
         ipcRenderer.on('fileData', (event, data) => { 
            document.write(data) 
         }) 
      </script> 
   </body> 
</html>

现在,每当我们运行我们的应用程序时,都会弹出一个本机打开对话框,如以下屏幕截图所示 -

打开对话框

一旦我们选择要显示的文件,其内容将显示在应用程序窗口上 -

使用对话框读取文件

这只是 Electron 提供的四个对话框之一。但它们都有相似的用法。一旦您了解了如何使用showOpenDialog来完成此操作,您就可以使用任何其他对话框。

具有相同功能的对话框是 -

  • showSaveDialog([浏览器窗口,]选项[,回调])
  • showMessageDialog([浏览器窗口,]选项[,回调])
  • 显示错误对话框(标题,内容)

Electron - 菜单

桌面应用程序带有两种类型的菜单 -应用程序菜单(位于顶部栏)和上下文菜单(右键单击菜单)。我们将在本章中学习如何创建这两者。

我们将使用两个模块——菜单菜单项模块。请注意,MenuMenuItem模块仅在主进程中可用。为了在渲染器进程中使用这些模块,您需要远程模块。当我们创建上下文菜单时,我们会遇到这个问题。

现在,让我们为主进程创建一个新的main.js文件 -

const {app, BrowserWindow, Menu, MenuItem} = require('electron')
const url = require('url')
const path = require('path')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

const template = [
   {
      label: 'Edit',
      submenu: [
         {
            role: 'undo'
         },
         {
            role: 'redo'
         },
         {
            type: 'separator'
         },
         {
            role: 'cut'
         },
         {
            role: 'copy'
         },
         {
            role: 'paste'
         }
      ]
   },
   
   {
      label: 'View',
      submenu: [
         {
            role: 'reload'
         },
         {
            role: 'toggledevtools'
         },
         {
            type: 'separator'
         },
         {
            role: 'resetzoom'
         },
         {
            role: 'zoomin'
         },
         {
            role: 'zoomout'
         },
         {
            type: 'separator'
         },
         {
            role: 'togglefullscreen'
         }
      ]
   },
   
   {
      role: 'window',
      submenu: [
         {
            role: 'minimize'
         },
         {
            role: 'close'
         }
      ]
   },
   
   {
      role: 'help',
      submenu: [
         {
            label: 'Learn More'
         }
      ]
   }
]

const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
app.on('ready', createWindow)

我们正在从此处的模板构建菜单。这意味着我们将菜单作为 JSON 提供给函数,它将处理其余的事情。现在我们必须将此菜单设置为应用程序菜单。

现在创建一个名为index.html的空HTML文件并使用以下命令运行该应用程序:

$ electron ./main.js

在应用程序菜单的正常位置,您将看到基于上述模板的菜单。

应用程序菜单

我们从主进程创建了这个菜单。现在让我们为我们的应用程序创建一个上下文菜单。我们将在 HTML 文件中执行此操作 -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Menus</title>
   </head>
   
   <body>
      <script type = "text/javascript">
         const {remote} = require('electron')
         const {Menu, MenuItem} = remote

         const menu = new Menu()

         // Build menu one item at a time, unlike
         menu.append(new MenuItem ({
            label: 'MenuItem1',
            click() { 
               console.log('item 1 clicked')
            }
         }))
         
         menu.append(new MenuItem({type: 'separator'}))
         menu.append(new MenuItem({label: 'MenuItem2', type: 'checkbox', checked: true}))
         menu.append(new MenuItem ({
            label: 'MenuItem3',
            click() {
               console.log('item 3 clicked')
            }
         }))

         // Prevent default action of right click in chromium. Replace with our menu.
         window.addEventListener('contextmenu', (e) => {
            e.preventDefault()
            menu.popup(remote.getCurrentWindow())
         }, false)
      </script>
   </body>
</html>

我们使用远程模块导入了 Menu 和 MenuItem 模块;然后,我们创建了一个菜单并将菜单项一一附加到其中。此外,我们阻止了 Chromium 中右键单击的默认操作,并将其替换为我们的菜单。

上下文菜单

在 Electron 中创建菜单是一项非常简单的任务。现在您可以将事件处理程序附加到这些项目并根据您的需要处理事件。

Electron - 系统托盘

系统托盘是应用程序窗口之外的菜单。在 MacOS 和 Ubuntu 上,它位于屏幕的右上角。在 Windows 上,它位于右下角。我们可以使用 Electron 在系统托盘中为我们的应用程序创建菜单。

创建一个新的main.js文件并向其中添加以下代码。准备一个可用于系统托盘图标的 png 文件。

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

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

设置基本浏览器窗口后,我们将创建一个新的index.html文件,其中包含以下内容 -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Menus</title>
   </head>
   <body>
      <script type = "text/javascript">
         const {remote} = require('electron')
         const {Tray, Menu} = remote
         const path = require('path')

         let trayIcon = new Tray(path.join('','/home/ayushgp/Desktop/images.png'))

         const trayMenuTemplate = [
            {
               label: 'Empty Application',
               enabled: false
            },
            
            {
               label: 'Settings',
               click: function () {
                  console.log("Clicked on settings")
               }
            },
            
            {
               label: 'Help',
               click: function () {
                  console.log("Clicked on Help")
               }
            }
         ]
         
         let trayMenu = Menu.buildFromTemplate(trayMenuTemplate)
         trayIcon.setContextMenu(trayMenu)
      </script>
   </body>
</html>

我们使用 Tray 子模块创建了托盘。然后,我们使用模板创建了一个菜单,并将菜单进一步附加到我们的托盘对象。

使用以下命令运行应用程序 -

$ electron ./main.js

当您运行上述命令时,请检查系统托盘中是否有您使用的图标。我在申请中使用了笑脸。上面的命令将生成以下输出 -

托盘

电子 - 通知

Electron 仅提供适用于 MacOS 的原生通知 API。所以我们不会使用它,而是使用一个名为node-notifier的 npm 模块。它允许我们通知 Windows、MacOS 和 Linux 上的用户。

使用该文件夹中的以下命令在您的应用程序文件夹中安装节点通知程序模块 -

$ npm install --save node-notifier

现在让我们创建一个具有按钮的应用程序,每次单击此按钮时都会生成通知。

创建一个新的main.js文件并在其中输入以下代码 -

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

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

现在让我们创建将触发通知的网页和脚本。使用以下代码创建一个新的index.html文件 -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Menus</title>
   </head>
   
   <body>
      <button type = "button" id = "notify" name = "button">
         Click here to trigger a notification!</button>
      <script type = "text/javascript">
         const notifier = require('node-notifier')
         const path = require('path');
         
         document.getElementById('notify').onclick = (event) => {
            notifier.notify ({
               title: 'My awesome title',
               message: 'Hello from electron, Mr. User!',
               icon: path.join('','/home/ayushgp/Desktop/images.png'),  // Absolute path 
                  (doesn't work on balloons)
               sound: true,  // Only Notification Center or Windows Toasters
               wait: true    // Wait with callback, until user action is taken 
               against notification
            
            }, function (err, response) {
               // Response is response from notification
            });

            notifier.on('click', function (notifierObject, options) {
               console.log("You clicked on the notification")
            });

            notifier.on('timeout', function (notifierObject, options) {
               console.log("Notification timed out!")
            });
         }
      </script>
   </body>
</html>

通知方法允许我们向其传递一个包含标题、消息、缩略图等信息的对象,这有助于我们自定义通知。我们还可以在通知上设置一些事件监听器。

现在,使用以下命令运行应用程序 -

$ electron ./main.js

当您单击我们创建的按钮时,您将看到来自操作系统的本机通知,如以下屏幕截图所示 -

通知

我们还处理了用户点击通知或通知超时的事件。如果应用程序在后台运行,这些方法可以帮助我们使应用程序更具交互性。

电子-Webview

webview 标签用于在 Electron 应用程序中嵌入“访客”内容,例如网页。此内容包含在 webview 容器中。应用程序中的嵌入页面控制此内容的显示方式。

webview 在与您的应用程序不同的进程中运行。为了确保免受恶意内容的安全,网络视图没有与您的网页相同的权限。这可以确保您的应用程序免受嵌入内容的影响。您的应用程序和嵌入页面之间的所有交互都将是异步的。

让我们考虑一个例子来了解外部网页在我们的 Electron 应用程序中的嵌入。我们将把tutorialspoint 网站嵌入到我们应用程序的右侧。使用以下内容创建一个新的main.js文件 -

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

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

现在我们已经设置了主流程,让我们创建将嵌入tutorialspoint 网站的 HTML 文件。创建一个名为 index.html 的文件,其中包含以下内容 -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Menus</title>
   </head>
   
   <body>
      <div>
         <div>
            <h2>We have the website embedded below!</h2>
         </div>
         <webview id = "foo" src = "https://www.tutorialspoint.com/" style = 
            "width:400px; height:480px;">
            <div class = "indicator"></div>
         </webview>
      </div>
      
      <script type = "text/javascript">
         // Event handlers for loading events.
         // Use these to handle loading screens, transitions, etc
         onload = () => {
            const webview = document.getElementById('foo')
            const indicator = document.querySelector('.indicator')

            const loadstart = () => {
               indicator.innerText = 'loading...'
            }

            const loadstop = () => {
               indicator.innerText = ''
            }

            webview.addEventListener('did-start-loading', loadstart)
            webview.addEventListener('did-stop-loading', loadstop)
         }
      </script>
   </body>
</html>

使用以下命令运行应用程序 -

$ electron ./main.js

上面的命令将生成以下输出 -

网页视图

webview 标签也可用于其他资源。webview 元素具有官方文档中列出的它发出的事件列表。您可以使用这些事件来根据 web 视图中发生的事情来改进功能。

每当您嵌入来自 Internet 的脚本或其他资源时,建议使用 webview。推荐这样做,因为它具有很大的安全优势并且不会妨碍正常Behave。

Electron - 音频和视频捕获

如果您正在构建用于屏幕共享、语音备忘录等的应用程序,音频和视频捕获是重要的特性。如果您需要应用程序来捕获个人资料图片,它们也很有用。

我们将使用getUserMedia HTML5 API 通过 Electron 捕获音频和视频流。让我们首先在main.js文件中设置主进程,如下所示 -

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

let win

// Set the path where recordings will be saved
app.setPath("userData", __dirname + "/saved_recordings")

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

app.on('ready', createWindow)

现在我们已经设置了主流程,让我们创建将捕获此内容的 HTML 文件。创建一个名为index.html的文件,其中包含以下内容 -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>Audio and Video</title>
   </head>
   
   <body>
      <video autoplay></video>
      <script type = "text/javascript">
         function errorCallback(e) {
            console.log('Error', e)
         }

         navigator.getUserMedia({video: true, audio: true}, (localMediaStream) => {
            var video = document.querySelector('video')
            video.src = window.URL.createObjectURL(localMediaStream)
            video.onloadedmetadata = (e) => {
               // Ready to go. Do some stuff.
            };
         }, errorCallback)
      </script>
   </body>
</html>

上述程序将生成以下输出 -

音视频流

您现在可以同时获得来自网络摄像头和麦克风的流。您可以通过网络发送此流或将其保存为您喜欢的格式。

查看有关捕获图像的MDN 文档,以从网络摄像头获取图像并存储它们。这是使用 HTML5 getUserMedia API 完成的。您还可以使用Electron 附带的desktopCapturer模块捕获用户桌面。现在让我们看一个如何获取屏幕流的示例。

使用与上面相同的 main.js 文件并编辑 index.html 文件以包含以下内容 -

desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
   if (error) throw error
   for (let i = 0; i < sources.length; ++i) {
      if (sources[i].name === 'Your Window Name here!') {
         navigator.webkitGetUserMedia({
            audio: false,
            video: {
               mandatory: {
                  chromeMediaSource: 'desktop',
                  chromeMediaSourceId: sources[i].id,
                  minWidth: 1280,
                  maxWidth: 1280,
                  minHeight: 720,
                  maxHeight: 720
               }
            }
         }, handleStream, handleError)
         return
      }
   }
})

function handleStream (stream) {
   document.querySelector('video').src = URL.createObjectURL(stream)
}

function handleError (e) {
   console.log(e)
}

我们使用desktopCapturer模块来获取有关每个打开的窗口的信息。现在,您可以根据传递给上述if 语句的名称捕获特定应用程序或整个屏幕的事件。这只会将屏幕上发生的情况传输到您的应用程序。

桌面捕捉器

你可以参考这个StackOverflow问题来了解详细的用法。

Electron - 定义快捷方式

我们通常会记住每天在电脑上使用的所有应用程序的某些快捷方式。为了让用户感觉应用程序直观且易于访问,您必须允许用户使用快捷方式。

我们将使用 globalShortcut 模块来定义应用程序中的快捷方式。请注意,加速器是可以包含多个修饰符和键代码的字符串,由 + 字符组合。这些加速器用于定义整个应用程序中的键盘快捷键。

让我们考虑一个例子并创建一个快捷方式。为此,我们将遵循对话框示例,其中我们使用打开对话框来打开文件。我们将注册一个CommandOrControl+O快捷方式来打开对话框。

我们的main.js代码将保持与以前相同。因此,创建一个新的main.js文件并在其中输入以下代码 -

const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')
const {ipcMain} = require('electron')

let win

function createWindow() {
   win = new BrowserWindow({width: 800, height: 600})
   win.loadURL(url.format ({
      pathname: path.join(__dirname, 'index.html'),
      protocol: 'file:',
      slashes: true
   }))
}

ipcMain.on('openFile', (event, path) => {
   const {dialog} = require('electron')
   const fs = require('fs')
   dialog.showOpenDialog(function (fileNames) {
         
      // fileNames is an array that contains all the selected
      if(fileNames === undefined)
         console.log("No file selected")
      else
         readFile(fileNames[0])
   })

   function readFile(filepath){
      fs.readFile(filepath, 'utf-8', (err, data) => {
         if(err){
            alert("An error ocurred reading the file :" + err.message)
            return
         }
         
         // handle the file content
         event.sender.send('fileData', data)
      })
   }
})

app.on('ready', createWindow)

每当我们的主进程从渲染器进程接收到“openFile”消息时,此代码就会弹出打开对话框。早些时候,每当应用程序运行时都会弹出此对话框。现在让我们限制它仅在按下CommandOrControl+O时打开。

现在创建一个包含以下内容的新index.html文件 -

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "UTF-8">
      <title>File read using system dialogs</title>
   </head>
   
   <body>
      <p>Press CTRL/CMD + O to open a file. </p>
      <script type = "text/javascript">
         const {ipcRenderer, remote} = require('electron')
         const {globalShortcut} = remote
         globalShortcut.register('CommandOrControl+O', () => {
            ipcRenderer.send('openFile', () => {
               console.log("Event sent.");
            })
            
            ipcRenderer.on('fileData', (event, data) => {
               document.write(data)
            })
         })
      </script>
   </body>
</html>

我们注册了一个新的快捷方式并传递了一个回调,每当我们按下该快捷方式时就会执行该回调。当我们不需要快捷方式时,我们可以取消注册它们。

现在,一旦打开应用程序,我们将收到使用刚刚定义的快捷方式打开文件的消息。

打开对话框

通过允许用户为定义的操作选择自己的快捷方式,可以对这些快捷方式进行自定义。

Electron - 环境变量

环境变量控制应用程序配置和Behave,而无需更改代码。某些 Electron Behave由环境变量控制,因为它们的初始化早于命令行标志和应用程序代码。

Electron 中编码了两种环境变量——生产变量开发变量。

生产变量

以下环境变量旨在在打包的 Electron 应用程序中运行时使用。

先生编号 变量和描述
1

GOOGLE_API_KEY

Electron 包含一个硬编码的 API 密钥,用于向 Google 的地理编码网络服务发出请求。由于这个 API 密钥包含在 Electron 的每个版本中,因此它经常超出其使用配额。

要解决此问题,您可以在环境中提供自己的 Google API 密钥。在打开任何将发出地理编码请求的浏览器窗口之前,将以下代码放入主进程文件中 -

process.env.GOOGLE_API_KEY = 'YOUR_KEY_HERE'
2

ELECTRON_RUN_AS_NODE

作为普通 Node.js 进程启动该进程。

3

ELECTRON_FORCE_WINDOW_MENU_BAR(仅限 Linux)

不要在 Linux 上使用全局菜单栏。

发展变量

以下环境变量主要用于开发和调试目的。

先生编号 变量和描述
1

电子_启用_记录

将 Chrome 的内部日志记录打印到控制台。

2

ELECTRON_ENABLE_STACK_DUMPING

当 Electron 崩溃时,将堆栈跟踪打印到控制台。

3

ELECTRON_DEFAULT_ERROR_MODE

当 Electron 崩溃时显示 Windows 的崩溃对话框。

要将这些环境变量中的任何一个设置为 true,请在控制台中进行设置。例如,如果您想启用日志记录,请使用以下命令 -

对于 Windows

> set ELECTRON_ENABLE_LOGGING=true

对于Linux

$ export ELECTRON_ENABLE_LOGGING=true

请注意,每次重新启动计算机时都需要设置这些环境变量。如果您想避免这样做,请将这些行添加到您的.bashrc文件中。

电子调试

我们有两个进程运行我们的应用程序——主进程和渲染器进程。

由于渲染器进程是在浏览器窗口中执行的进程,因此我们可以使用 Chrome Devtools 来调试它。要打开 DevTools,请使用快捷键“Ctrl+Shift+I”或 <F12> 键。您可以在此处查看如何使用 devtools 。

当您打开 DevTools 时,您的应用程序将如下图所示 -

开发工具

调试主进程

Electron 浏览器窗口中的 DevTools 只能调试在该窗口(即网页)中执行的 JavaScript。要调试在主进程中执行的 JavaScript,您需要使用外部调试器并使用 --debug--debug -brk开关启动 Electron。

Electron将在指定端口监听V8调试器协议消息;外部调试器需要连接到此端口。默认端口为 5858。

使用以下命令运行您的应用程序 -

$ electron --debug = 5858 ./main.js

现在您需要一个支持 V8 调试器协议的调试器。您可以使用 VSCode 或节点检查器来实现此目的。例如,让我们按照以下步骤操作并为此目的设置 VSCode。请按照以下步骤进行设置 -

下载并安装VSCode。在 VSCode 中打开您的 Electron 项目。

使用以下配置添加文件.vscode/launch.json -

{
   "version": "1.0.0",
   "configurations": [
      {
         "name": "Debug Main Process",
         "type": "node",
         "request": "launch",
         "cwd": "${workspaceRoot}",
         "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
         "program": "${workspaceRoot}/main.js"
      }
   ]
}

注意- 对于 Windows,请使用“${workspaceRoot}/node_modules/.bin/electron.cmd”作为runtimeExecutable

在main.js中设置一些断点,然后在调试视图中开始调试。当您到达断点时,屏幕将如下所示 -

调试器

VSCode 调试器非常强大,可以帮助您快速纠正错误。您还可以使用其他选项,例如用于调试电子应用程序的节点检查器。

电子 - 包装应用程序

打包和分发应用程序是桌面应用程序开发过程中不可或缺的一部分。由于 Electron 是一个跨平台的桌面应用程序开发框架,因此所有平台的应用程序打包和分发也应该是无缝的体验。

电子社区创建了一个项目,电子打包器,为我们处理同样的事情。它允许我们通过 JS 或 CLI 与特定于操作系统的捆绑包(.app、.exe 等)打包和分发我们的 Electron 应用程序。

支持的平台

Electron Packager 在以下主机平台上运行 -

  • Windows(32/64 位)
  • 操作系统
  • Linux (x86/x86_64)

它为以下目标平台生成可执行文件/捆绑包 -

  • Windows(也称为 win32,适用于 32/64 位)
  • OS X(也称为 darwin)/Mac App Store(也称为 mas)
  • Linux(适用于 x86、x86_64 和 armv7l 架构)

安装

使用安装电子打包器 -

# for use in npm scripts
$ npm install electron-packager --save-dev

# for use from cli
$ npm install electron-packager -g

打包应用程序

在本节中,我们将了解如何从命令行运行打包程序。命令的基本形式是 -

electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...]

这将 -

  • 查找或下载正确版本的 Electron。

  • 使用该版本的 Electron 在 <output-folder>/<appname>-<platform>-<arch> 中创建应用程序。

在两种情况下, --platform--arch可以省略。如果您指定--all,则将为目标平台/体系结构的所有有效组合创建捆绑包。否则,将为主机平台/架构创建单个捆绑包。

电子 - 资源

我们使用以下资源来了解有关 Electron 的更多信息。我们在创建本教程时参考了这些。

最重要的资源是Electron 文档。该文档广泛涵盖了该框架的几乎所有功能和怪癖。他们足以独自完成应用程序的构建。

electronics-sample-apps存储库中还提供了一些非常好的 Electron 示例。

视频资源

具有网络语言的桌面应用程序

使用 JavaScript 和 Electron 快速跨平台桌面应用程序开发

博客文章

使用 Electron 构建桌面应用程序

使用 React 和 Electron 构建音乐播放器

使用 HTML、JS 和 Electron 创建您的第一个桌面应用程序

使用 Electron 创建跨平台桌面节点应用程序