目录

Electron - 快速指南

Electron - Overview

为何选择电子?

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

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

谁使用电子?

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

Electron已被用于创建许多应用程序。 以下是一些值得注意的应用程序 -

  • Slack desktop
  • Wordpress桌面应用
  • Visual Studio代码
  • Caret Markdown编辑
  • Nylas电子邮件应用
  • GitKraken git客户端

Electron - Installation

要开始使用Electron进行开发,您需要安装Node和npm(节点包管理器)。 如果您还没有这些,请转到Node setup以在本地系统上安装节点。 通过在终端中运行以下命令,确认已安装节点和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 - How Electron Works

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

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

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

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

Main和Renderer进程之间的区别

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

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

Electron - Hello World

我们为项目创建了一个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

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

电子Hello,World!

这个应用程序如何工作?

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

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

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

Electron - Building UIs

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文件中将required要这样做。 我们已经有一个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

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

UI

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

Electron - File Handling

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

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

使用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()函数后,我们将在add to list按钮上创建一个单击处理程序。 这会将条目添加到文件和表中。

在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 module API calls ,请参阅Node File System tutorial

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

Electron - Native Node Libraries

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

OS模块

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

S.No 功能说明
1

os.userInfo([options])

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

2

os.platform()

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

3

os.homedir()

os.homedir()方法以字符串形式返回当前用户的主目录。 通常,所有用户的配置都驻留在用户的主目录中。 所以这可以用于我们的应用程序的相同目的。

4

os.arch()

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

5

os.EOL

定义特定于操作系统的终止标记的字符串常量。 只要在主机操作系统上的文件中结束行,就应该使用它。

使用相同的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

网络模块

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

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

S.No 功能说明
1

net.createServer([options][, connectionListener])

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

2

net.createConnection(options[, connectionListener])

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

3

net.Server.listen(port[, host][, backlog][, callback])

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

4

net.Server.close([callback])

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

5

net.Socket.connect(port[, host][, connectListener])

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

网络模块也带有一些其他方法。 要获得更全面的列表,请参阅this

现在,让我们创建一个电子应用程序,它使用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 - Inter Process Communication

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 - System Dialogs

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

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

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

创建一个新的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([browserWindow, ]options[, callback])
  • showMessageDialog([browserWindow, ]options[, callback])
  • showErrorDialog(title, content)

Electron - Menus

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

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

现在,让我们为主进程创建一个新的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模块; 然后,我们创建了一个菜单,并逐个添加我们的菜单项。 此外,我们阻止了在铬中右键单击的默认操作,并将其替换为我们的菜单。

上下文菜单

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

Electron - System Tray

系统托盘是应用程序窗口之外的菜单。 在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 - Notifications

Electron仅为MacOS提供本机通知API。 所以我们不打算使用它,而是使用名为node-notifier的npm模块。 它允许我们在Windows,MacOS和Linux上通知用户。

使用该文件夹中的以下命令在app文件夹中安装node-notifier模块 -

$ 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>

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

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

$ electron ./main.js

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

通知

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

Electron - Webview

webview标记用于在您的Electron应用中嵌入“来宾”内容,例如网页。 此内容包含在webview容器中。 应用内的嵌入式页面控制着如何显示此内容。

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

让我们考虑一个示例来了解在我们的Electron应用程序中嵌入外部网页。 我们将在我们的应用程序中嵌入iowiki网站的右侧。 使用以下内容创建一个新的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)

现在我们已经设置了主要流程,让我们创建将嵌入iowiki网站的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.iowiki.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元素包含在官方文档中列出的事件列表。 您可以使用这些事件来改进功能,具体取决于webview中发生的事情。

无论何时从Internet嵌入脚本或其他资源,建议使用webview。 建议使用它,因为它具有很高的安全性,并且不会妨碍正常行为。

Electron - Audio and Video Capturing

如果您要为屏幕共享,语音备忘录等构建应用程序,音频和视频捕获是重要的特性。如果您需要应用程序来捕获配置文件图片,它们也很有用。

我们将使用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 statement 。 这将仅将该屏幕上发生的内容流式传输到您的应用。

桌面捕获器

您可以参考此StackOverflow问题以详细了解其用法。

Electron - Defining Shortcuts

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

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

让我们考虑一个示例并创建一个快捷方式。 为此,我们将按照对话框示例,我们使用打开的对话框打开文件。 我们将注册一个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 - Environment Variables

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

电子编码有两种环境变量 - Production variablesDevelopment variables.

生产变量

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

S.No 变量和描述
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 Only)

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

发展变量

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

S.No 变量和描述
1

ELECTRON_ENABLE_LOGGING

将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文件中。

Electron - Debugging

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

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

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

DevTools

调试主进程

电子浏览器窗口中的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"
      }
   ]
}

Note - 对于Windows,对runtimeExecutable使用"${workspaceRoot}/node_modules/.bin/electron.cmd"

main.js设置一些断点,然后在Debug View中开始调试。 当你点击断点时,屏幕看起来像这样 -

调试器

VSCode调试器非常强大,可以帮助您快速纠正错误。 您还可以使用其他选项,例如node-inspector来调试电子应用程序。

Electron - Packaging Apps

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

电子社区已经创建了一个项目, electron-packager ,为我们照顾同样的事情。 它允许我们通过JS或CLI打包和分发我们的Electron应用程序与特定于操作系统的软件包(.app,.exe等)。

支持的平台

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

  • Windows(32/64位)
  • OS X.
  • Linux(x86/x86_64)

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

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

安装 (Installation)

使用 - 安装电子封装器 -

# 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在/ - - 中创建应用程序。

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

Electron - Resources

我们使用以下资源来了解有关Electron的更多信息。 我们在创建本教程时已经提到过这些内容。

最重要的资源是Electron文档。 文档广泛涵盖了框架的几乎所有功能和怪癖。 他们足够孤独,可以通过构建应用程序。

electron-sample-apps中还有一些非常好的Electron示例。

视频资源

使用网络语言的桌面应用

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

博客帖子

使用Electron构建桌面应用程序

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

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

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

↑回到顶部↑
WIKI教程 @2018