Electron: кроссплатформенные настольные приложения стали проще
Опубликовано: 2022-03-11Ранее в этом году Github выпустил Atom-Shell, ядро своего знаменитого редактора с открытым исходным кодом Atom, и по особому случаю переименовал его в Electron .
Electron, в отличие от других конкурентов в категории настольных приложений на основе Node.js, привносит свою изюминку в этот уже хорошо зарекомендовавший себя рынок, сочетая мощь Node.js (io.js до недавних выпусков) с Chromium Engine, чтобы принести мы используем лучшее из серверного и клиентского JavaScript.
Представьте себе мир, в котором мы могли бы создавать производительные, управляемые данными кроссплатформенные настольные приложения, основанные не только на постоянно растущем репозитории модулей NPM, но и на всем реестре Bower для удовлетворения всех наших потребностей на стороне клиента.
Введите Электрон.
В этом руководстве мы создадим простое приложение для цепочки ключей с паролем, используя Electron, Angular.js и Loki.js, легкую базу данных в памяти со знакомым синтаксисом для разработчиков MongoDB.
Полный исходный код этого приложения доступен здесь.
В этом руководстве предполагается, что:
- На компьютере читателя установлены Node.js и Bower.
- Они знакомы с синтаксисом запросов Node.js, Angular.js и MongoDB.
Получение товара
Прежде всего, нам нужно получить двоичные файлы Electron, чтобы протестировать наше приложение локально. Мы можем установить его глобально и использовать в качестве интерфейса командной строки или установить локально по пути нашего приложения. Я рекомендую установить его глобально, чтобы нам не приходилось делать это снова и снова для каждого приложения, которое мы разрабатываем.
Позже мы узнаем, как упаковать наше приложение для распространения с помощью Gulp. Этот процесс включает в себя копирование двоичных файлов Electron, и поэтому практически нет смысла вручную устанавливать его по пути нашего приложения.
Чтобы установить Electron CLI, мы можем ввести следующую команду в нашем терминале:
$ npm install -g electron-prebuilt
Чтобы проверить установку, введите electron -h
, и он должен отобразить версию интерфейса командной строки Electron.
На момент написания этой статьи версия Electron была 0.31.2
.
Настройка проекта
Давайте предположим следующую базовую структуру папок:
my-app |- cache/ |- dist/ |- src/ |-- app.js | gulpfile.js
… где: - cache/ будет использоваться для загрузки бинарных файлов Electron при сборке приложения. - dist/ будет содержать сгенерированные файлы дистрибутива. - src/ будет содержать наш исходный код. - src/app.js будет точкой входа нашего приложения.
Затем мы перейдем к папке src/
в нашем терминале и создадим файлы package.json
и bower.json
для нашего приложения:
$ npm init $ bower init
Мы установим необходимые пакеты позже в этом руководстве.
Понимание электронных процессов
Электрон различает два типа процессов:
- Основной процесс : точка входа нашего приложения, файл, который будет выполняться всякий раз, когда мы запускаем приложение. Как правило, этот файл объявляет различные окна приложения и может дополнительно использоваться для определения прослушивателей глобальных событий с помощью модуля IPC Electron.
- Процесс рендеринга : Контроллер для данного окна в нашем приложении. Каждое окно создает свой собственный процесс визуализации.
Для ясности кода для каждого процесса визуализации следует использовать отдельный файл. Чтобы определить основной процесс для нашего приложения, мы откроем
src/app.js
и включим модульapp
для запуска приложения и модульbrowser-window
для создания различных окон нашего приложения (оба являются частью ядра Electron). в качестве таких:
var app = require('app'), BrowserWindow = require('browser-window');
Когда приложение фактически запущено, оно запускает событие ready
, к которому мы можем привязаться. На этом этапе мы можем создать экземпляр главного окна нашего приложения:
var mainWindow = null; app.on('ready', function() { mainWindow = new BrowserWindow({ width: 1024, height: 768 }); mainWindow.loadUrl('file://' + __dirname + '/windows/main/main.html'); mainWindow.openDevTools(); });
Ключевые моменты:
- Мы создаем новое окно, создавая новый экземпляр объекта
BrowserWindow
. - Он принимает объект в качестве единственного аргумента, что позволяет нам определять различные параметры, среди которых ширина и высота окна по умолчанию.
- Экземпляр окна имеет метод
loadUrl()
, позволяющий нам загружать содержимое фактического HTML-файла в текущем окне. Файл HTML может быть локальным или удаленным . - Экземпляр окна имеет необязательный
openDevTools()
, позволяющий нам открывать экземпляр Chrome Dev Tools в текущем окне для целей отладки.
Далее нам следует немного упорядочить наш код. Я рекомендую создать папку windows/
в нашей папке src/
, и где мы можем создать подпапку для каждого окна, как таковую:
my-app |- src/ |-- windows/ |--- main/ |---- main.controller.js |---- main.html |---- main.view.js
… где main.controller.js
будет содержать «серверную» логику нашего приложения, а main.view.js
будет содержать «клиентскую» логику нашего приложения.
Файл main.html
— это просто веб-страница HTML5, поэтому мы можем просто запустить ее следующим образом:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Password Keychain</title> </head> <body> <h1>Password Keychain</h1> </body> </html>
На этом этапе наше приложение должно быть готово к запуску. Чтобы проверить это, мы можем просто ввести следующее в нашем терминале в корне папки src
:
$ electron .
Мы можем автоматизировать этот процесс, определив сценарий
start
файла package.son.
Создание настольного приложения связки ключей с паролем
Для создания приложения связки паролей нам потребуются: - Способ добавлять, генерировать и сохранять пароли. - Удобный способ копирования и удаления паролей.
Генерация и сохранение паролей
Для ввода новых паролей будет достаточно простой формы. Чтобы продемонстрировать связь между несколькими окнами в Electron, начните с добавления второго окна в наше приложение, которое будет отображать форму «вставки». Поскольку мы будем открывать и закрывать это окно несколько раз, нам следует заключить логику в метод, чтобы мы могли просто вызывать его при необходимости:
function createInsertWindow() { insertWindow = new BrowserWindow({ width: 640, height: 480, show: false }); insertWindow.loadUrl('file://' + __dirname + '/windows/insert/insert.html'); insertWindow.on('closed',function() { insertWindow = null; }); }
Ключевые моменты:
- Нам нужно будет установить для свойства show значение false в объекте параметров конструктора BrowserWindow, чтобы предотвратить открытие окна по умолчанию при запуске приложений.
- Нам нужно будет уничтожать экземпляр BrowserWindow всякий раз, когда окно запускает закрытое событие.
Открытие и закрытие окна «Вставка»
Идея состоит в том, чтобы иметь возможность запускать окно «вставки», когда конечный пользователь нажимает кнопку в «главном» окне. Для этого нам нужно будет отправить сообщение из главного окна в основной процесс, чтобы указать ему открыть окно вставки. Мы можем добиться этого, используя модуль IPC Electron. На самом деле существует два варианта модуля IPC:
- Один для основного процесса, позволяющий приложению подписываться на сообщения, отправляемые из окон.
- Один для процесса визуализации, позволяющий приложению отправлять сообщения основному процессу.
Хотя канал связи Electron в основном однонаправленный, можно получить доступ к модулю IPC основного процесса в процессе рендерера, используя удаленный модуль. Кроме того, основной процесс может отправить сообщение обратно процессу визуализации, из которого возникло событие, с помощью метода Event.sender.send().
Чтобы использовать модуль IPC, он нам просто нужен, как и любой другой модуль NPM в нашем сценарии основного процесса:
var ipc = require('ipc');
… а затем привязать к событиям с помощью метода on()
:
ipc.on('toggle-insert-view', function() { if(!insertWindow) { createInsertWindow(); } return (!insertWindow.isClosed() && insertWindow.isVisible()) ? insertWindow.hide() : insertWindow.show(); });
Ключевые моменты:
- Мы можем назвать событие как угодно, пример просто произвольный.
- Не забудьте проверить, создан ли экземпляр BrowserWindow, если нет, то создайте его.
- Экземпляр BrowserWindow имеет несколько полезных методов:
- isClosed() возвращает логическое значение независимо от того, находится ли окно в настоящее время в
closed
состоянии. - isVisible() : возвращает логическое значение независимо от того, видимо окно в данный момент или нет.
- show() / hide() : удобные методы для отображения и скрытия окна.
- isClosed() возвращает логическое значение независимо от того, находится ли окно в настоящее время в
Теперь нам действительно нужно запустить это событие из процесса визуализации. Мы создадим новый файл скрипта с именем main.view.js
и добавим его на нашу HTML-страницу, как и любой обычный скрипт:
<script src="./main.view.js"></script>
Загрузка файла сценария с помощью тега
script
HTML загружает этот файл в контексте на стороне клиента. Это означает, что, например, глобальные переменные доступны черезwindow.<varname>
. Чтобы загрузить скрипт в контексте на стороне сервера, мы можем использовать методrequire()
непосредственно на нашей HTML-странице:require('./main.controller.js');
.
Несмотря на то, что скрипт загружается в контексте на стороне клиента , мы по-прежнему можем получить доступ к модулю IPC для процесса визуализации так же, как и для основного процесса, а затем отправить наше событие как таковое:
var ipc = require('ipc'); angular .module('Utils', []) .directive('toggleInsertView', function() { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); ipc.send('toggle-insert-view'); }); }; });
Также доступен метод sendSync() на случай, если нам нужно отправлять наши события синхронно.
Теперь все, что нам осталось сделать, чтобы открыть окно «вставки», — это создать HTML-кнопку с соответствующей директивой Angular:
<div ng-controller="MainCtrl as vm"> <button toggle-insert-view class="mdl-button"> <i class="material-icons">add</i> </button> </div>
И добавьте эту директиву как зависимость контроллера Angular главного окна:
angular .module('MainWindow', ['Utils']) .controller('MainCtrl', function() { var vm = this; });
Генерация паролей
Для простоты мы можем просто использовать модуль uuid
NPM для создания уникальных идентификаторов, которые будут действовать как пароли для целей этого руководства. Мы можем установить его, как и любой другой модуль NPM, потребовать его в нашем скрипте «Utils», а затем создать простую фабрику, которая будет возвращать уникальный идентификатор:
var uuid = require('uuid'); angular .module('Utils', []) ... .factory('Generator', function() { return { create: function() { return uuid.v4(); } }; })
Теперь все, что нам осталось сделать, это создать кнопку в представлении вставки и прикрепить к ней директиву, которая будет прослушивать события нажатия на кнопку и вызывать метод create():
<!-- in insert.html --> <button generate-password class="mdl-button">generate</button>
// in Utils.js angular .module('Utils', []) ... .directive('generatePassword', ['Generator', function(Generator) { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); if(!scope.vm.formData) scope.vm.formData = {}; scope.vm.formData.password = Generator.create(); scope.$apply(); }); }; }])
Сохранение паролей
На данный момент мы хотим сохранить наши пароли. Структура данных для наших записей паролей довольно проста:
{ "id": String "description": String, "username": String, "password": String }
Так что все, что нам действительно нужно, — это какая-то база данных в памяти, которая может дополнительно синхронизироваться с файлом для резервного копирования. Для этой цели Loki.js кажется идеальным кандидатом. Он делает именно то, что нам нужно для целей этого приложения, и предлагает поверх него функцию динамических представлений, позволяющую нам делать вещи, аналогичные модулю агрегации MongoDB.
Динамические представления не предлагают всех функций, которые предлагает модуль агрегации MongodDB. Пожалуйста, обратитесь к документации для получения дополнительной информации.
Начнем с создания простой HTML-формы:
<div class="insert" ng-controller="InsertCtrl as vm"> <form name="insertForm" no-validate> <fieldset ng-disabled="!vm.loaded"> <div class="mdl-textfield"> <input class="mdl-textfield__input" type="text" ng-model="vm.formData.description" required /> <label class="mdl-textfield__label" for="description">Description...</label> </div> <div class="mdl-textfield"> <input class="mdl-textfield__input" type="text" ng-model="vm.formData.username" /> <label class="mdl-textfield__label" for="username">Username...</label> </div> <div class="mdl-textfield"> <input class="mdl-textfield__input" type="password" ng-model="vm.formData.password" required /> <label class="mdl-textfield__label" for="password">Password...</label> </div> <div class=""> <button generate-password class="mdl-button">generate</button> <button toggle-insert-view class="mdl-button">cancel</button> <button save-password class="mdl-button" ng-disabled="insertForm.$invalid">save</button> </div> </fieldset> </form> </div>
А теперь давайте добавим логику JavaScript для обработки публикации и сохранения содержимого формы:

var loki = require('lokijs'), path = require('path'); angular .module('Utils', []) ... .service('Storage', ['$q', function($q) { this.db = new loki(path.resolve(__dirname, '../..', 'app.db')); this.collection = null; this.loaded = false; this.init = function() { var d = $q.defer(); this.reload() .then(function() { this.collection = this.db.getCollection('keychain'); d.resolve(this); }.bind(this)) .catch(function(e) { // create collection this.db.addCollection('keychain'); // save and create file this.db.saveDatabase(); this.collection = this.db.getCollection('keychain'); d.resolve(this); }.bind(this)); return d.promise; }; this.addDoc = function(data) { var d = $q.defer(); if(this.isLoaded() && this.getCollection()) { this.getCollection().insert(data); this.db.saveDatabase(); d.resolve(this.getCollection()); } else { d.reject(new Error('DB NOT READY')); } return d.promise; }; }) .directive('savePassword', ['Storage', function(Storage) { return function(scope, el) { el.bind('click', function(e) { e.preventDefault(); if(scope.vm.formData) { Storage .addDoc(scope.vm.formData) .then(function() { // reset form & close insert window scope.vm.formData = {}; ipc.send('toggle-insert-view'); }); } }); }; }])
Ключевые моменты:
- Сначала нам нужно инициализировать базу данных. Этот процесс включает в себя создание нового экземпляра объекта Loki, предоставление пути к файлу базы данных в качестве аргумента, поиск, существует ли этот файл резервной копии, его создание при необходимости (включая коллекцию «Связка ключей»), а затем загрузку содержимого этот файл в памяти.
- Мы можем получить определенную коллекцию в базе данных с помощью
getCollection()
. - Объект коллекции предоставляет несколько методов, в том числе метод
insert()
, позволяющий нам добавить новый документ в коллекцию. - Чтобы сохранить содержимое базы данных в файл, объект Loki предоставляет метод
saveDatabase()
. - Нам нужно будет сбросить данные формы и отправить событие IPC, чтобы указать основному процессу закрыть окно после сохранения документа.
Теперь у нас есть простая форма, позволяющая нам генерировать и сохранять новые пароли. Вернемся к основному виду, чтобы перечислить эти записи.
Список паролей
Здесь должно произойти несколько вещей:
- Нам нужно иметь возможность получить все документы в нашей коллекции.
- Нам нужно информировать главное представление всякий раз, когда сохраняется новый пароль, чтобы он мог обновить представление.
Мы можем получить список документов, вызвав метод getCollection()
для объекта Loki. Этот метод возвращает объект со свойством data , которое представляет собой просто массив всех документов в этой коллекции:
this.getCollection = function() { this.collection = this.db.getCollection('keychain'); return this.collection; }; this.getDocs = function() { return (this.getCollection()) ? this.getCollection().data : null; };
Затем мы можем вызвать getDocs() в нашем контроллере Angular и получить все пароли, хранящиеся в базе данных, после того, как мы ее инициализируем:
angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); }); });
Немного шаблонов Angular, и у нас есть список паролей:
<tr ng-repeat="item in vm.keychain track by $index" class="item--{{$index}}"> <td class="mdl-data-table__cell--non-numeric">{{item.description}}</td> <td>{{item.username || 'n/a'}}</td> <td> <span ng-repeat="n in [1,2,3,4,5,6]">•</span> </td> <td> <a href="#" copy-password="{{$index}}">copy</a> <a href="#" remove-password="{{item}}">remove</a> </td> </tr>
Приятной добавленной функцией было бы обновление списка паролей после вставки нового. Для этого мы можем использовать модуль IPC Electron. Как упоминалось ранее, модуль IPC основного процесса может быть вызван в процессе визуализации, чтобы превратить его в процесс слушателя с помощью удаленного модуля. Вот пример того, как это реализовать в main.view.js
:
var remote = require('remote'), remoteIpc = remote.require('ipc'); angular .module('MainView', ['Utils']) .controller('MainCtrl', ['Storage', function(Storage) { var vm = this; vm.keychain = null; Storage .init() .then(function(db) { vm.keychain = db.getDocs(); remoteIpc.on('update-main-view', function() { Storage .reload() .then(function() { vm.keychain = db.getDocs(); }); }); }); }]);
Ключевые моменты:
- Нам нужно будет использовать удаленный модуль через его собственный метод
require()
, чтобы запросить удаленный модуль IPC из основного процесса. - Затем мы можем настроить наш процесс визуализации в качестве прослушивателя событий с помощью метода
on()
и привязать функции обратного вызова к этим событиям.
Затем представление вставки будет отвечать за отправку этого события при каждом сохранении нового документа:
Storage .addDoc(scope.vm.formData) .then(function() { // refresh list in main view ipc.send('update-main-view'); // reset form & close insert window scope.vm.formData = {}; ipc.send('toggle-insert-view'); });
Копирование паролей
Обычно не рекомендуется отображать пароли в виде простого текста. Вместо этого мы собираемся скрыть и предоставить удобную кнопку, позволяющую конечному пользователю скопировать пароль непосредственно для определенной записи.
И здесь Electron снова приходит нам на помощь, предоставляя модуль буфера обмена с простыми методами копирования и вставки не только текстового содержимого, но также изображений и HTML-кода:
var clipboard = require('clipboard'); angular .module('Utils', []) ... .directive('copyPassword', [function() { return function(scope, el, attrs) { el.bind('click', function(e) { e.preventDefault(); var text = (scope.vm.keychain[attrs.copyPassword]) ? scope.vm.keychain[attrs.copyPassword].password : ''; // atom's clipboard module clipboard.clear(); clipboard.writeText(text); }); }; }]);
Поскольку сгенерированный пароль будет простой строкой, мы можем использовать метод writeText()
для копирования пароля в системный буфер обмена. Затем мы можем обновить наш HTML-код основного представления и добавить кнопку копирования с директивой copy-password
, указав индекс массива паролей:
<a href="#" copy-password="{{$index}}">copy</a>
Удаление паролей
Наши конечные пользователи также могут захотеть иметь возможность удалять пароли, если они устареют. Для этого все, что нам нужно сделать, это вызвать метод remove()
для коллекции связки ключей. Нам нужно предоставить весь документ методу remove(), как таковой:
this.removeDoc = function(doc) { return function() { var d = $q.defer(); if(this.isLoaded() && this.getCollection()) { // remove the doc from the collection & persist changes this.getCollection().remove(doc); this.db.saveDatabase(); // inform the insert view that the db content has changed ipc.send('reload-insert-view'); d.resolve(true); } else { d.reject(new Error('DB NOT READY')); } return d.promise; }.bind(this); };
В документации Loki.js указано, что мы также можем удалить документ по его идентификатору, но, похоже, это не работает должным образом.
Создание меню рабочего стола
Electron легко интегрируется со средой рабочего стола нашей ОС, чтобы обеспечить «родной» пользовательский интерфейс для наших приложений. Поэтому Electron поставляется в комплекте с модулем Menu, предназначенным для создания сложных структур меню рабочего стола для нашего приложения.
Модуль меню — это обширная тема, и она почти заслуживает отдельного руководства. Я настоятельно рекомендую вам прочитать учебник Electron по интеграции среды рабочего стола, чтобы узнать обо всех функциях этого модуля.
В рамках этого текущего руководства мы увидим, как создать пользовательское меню, добавить в него пользовательскую команду и реализовать стандартную команду выхода.
Создание и назначение пользовательского меню нашему приложению
Как правило, логика JavaScript для меню Electron находится в основном файле сценария нашего приложения, где определен наш основной процесс. Однако мы можем абстрагировать его в отдельный файл и получить доступ к модулю Menu через удаленный модуль:
var remote = require('remote'), Menu = remote.require('menu');
Чтобы определить простое меню, нам нужно будет использовать метод buildFromTemplate()
:
var appMenu = Menu.buildFromTemplate([ { label: 'Electron', submenu: [{ label: 'Credits', click: function() { alert('Built with Electron & Loki.js.'); } }] } ]);
Первый элемент массива всегда используется как пункт меню «по умолчанию».
Значение свойства
label
не имеет большого значения для пункта меню по умолчанию. В режиме разработки он всегда будет отображатьElectron
. Позже мы увидим, как назначить пользовательское имя элементу меню по умолчанию на этапе сборки.
Наконец, нам нужно назначить это пользовательское меню в качестве меню по умолчанию для нашего приложения с помощью setApplicationMenu()
:
Menu.setApplicationMenu(appMenu);
Сопоставление сочетаний клавиш
Electron предоставляет «ускорители», набор предопределенных строк, которые соответствуют фактическим комбинациям клавиш, например: Command+A
или Ctrl+Shift+Z
.
Ускоритель
Command
не работает в Windows или Linux. Для нашего приложения связки паролей мы должны добавить пункт меню «File
», предлагающий две команды:
- Создать пароль : откройте вид вставки с помощью Cmd (или Ctrl) + N
- Выйти : полностью выйти из приложения с помощью Cmd (или Ctrl) + Q
... { label: 'File', submenu: [ { label: 'Create Password', accelerator: 'CmdOrCtrl+N', click: function() { ipc.send('toggle-insert-view'); } }, { type: 'separator' // to create a visual separator }, { label: 'Quit', accelerator: 'CmdOrCtrl+Q', selector: 'terminate:' // OS X only!!! } ] } ...
Ключевые моменты:
- Мы можем добавить визуальный разделитель, добавив элемент в массив со свойством
type
, установленным вseparator
. -
CmdOrCtrl
совместим с клавиатурами Mac и ПК. - Свойство
selector
совместимо только с OSX!
Стилизация нашего приложения
Вы, наверное, заметили в различных примерах кода ссылки на имена классов, начинающиеся с mdl-
. Для целей этого руководства я решил использовать инфраструктуру пользовательского интерфейса Material Design Lite, но не стесняйтесь использовать любую инфраструктуру пользовательского интерфейса по вашему выбору.
Все, что мы можем сделать с HTML5, можно сделать и в Electron; просто имейте в виду растущий размер двоичных файлов приложения и связанные с этим проблемы с производительностью, которые могут возникнуть, если вы используете слишком много сторонних библиотек.
Упаковка приложений Electron для распространения
Вы сделали приложение Electron, оно выглядит великолепно, вы написали свои e2e-тесты с помощью Selenium и WebDriver, и вы готовы распространять его по всему миру!
Но вы все еще хотите персонализировать его, дать ему собственное имя, отличное от «Электрона» по умолчанию, и, возможно, также предоставить пользовательские значки приложений для платформ Mac и ПК.
Создание с помощью Gulp
В наши дни есть плагин Gulp для всего, что мы можем придумать. Все, что мне нужно было сделать, это ввести gulp electron
в Google, и, конечно же, есть плагин gulp-electron!
Этот плагин довольно прост в использовании, если сохраняется структура папок, описанная в начале этого руководства. Если нет, возможно, вам придется немного переместить вещи.
Этот плагин можно установить, как и любой другой плагин Gulp:
$ npm install gulp-electron --save-dev
И тогда мы можем определить нашу задачу Gulp как таковую:
var gulp = require('gulp'), electron = require('gulp-electron'), info = require('./src/package.json'); gulp.task('electron', function() { gulp.src("") .pipe(electron({ src: './src', packageJson: info, release: './dist', cache: './cache', version: 'v0.31.2', packaging: true, platforms: ['win32-ia32', 'darwin-x64'], platformResources: { darwin: { CFBundleDisplayName: info.name, CFBundleIdentifier: info.bundle, CFBundleName: info.name, CFBundleVersion: info.version }, win: { "version-string": info.version, "file-version": info.version, "product-version": info.version } } })) .pipe(gulp.dest("")); });
Ключевые моменты:
- папка
src/
не может совпадать ни с папкой, в которой находится Gulpfile.js, ни с той же папкой, что и папка дистрибутива. - Мы можем определить платформы, на которые мы хотим экспортировать через массив
platforms
. - Мы должны определить папку
cache
, куда будут загружаться бинарные файлы Electron, чтобы их можно было упаковать с нашим приложением. - Содержимое файла package.json приложения необходимо передать задаче
packageJson
через свойство packageJson. - Существует необязательное свойство
packaging
, позволяющее нам также создавать zip-архивы сгенерированных приложений. - Для каждой платформы существует свой набор «ресурсов платформы», которые можно определить.
Добавление значков приложений
Одним из свойств platformResources
является свойство icon
, позволяющее нам определить собственный значок для нашего приложения:
"icon": "keychain.ico"
Для OS X требуются значки с расширением файла
.icns
. Существует несколько онлайн-инструментов, позволяющих бесплатно конвертировать файлы.png
в.ico
и.icns
.
Заключение
В этой статье мы только коснулись того, что на самом деле может сделать Electron. Воспринимайте отличные приложения, такие как Atom или Slack, как источник вдохновения, в котором вы можете использовать этот инструмент.
Я надеюсь, что вы нашли этот урок полезным, пожалуйста, не стесняйтесь оставлять свои комментарии и делиться своим опытом работы с Electron!