Содержание статьи
- Что будем делать
- Подготовка
- Иконки
- Node.js
- Операционная система
- Минимальное приложение Electron
- Виджет SoundCloud
- API виджета
- Добавление виджета на страницу
- Инициализация API виджета SoundCloud
- Методы и события API виджета SoundCloud
- Иконка в области уведомлений
- Добавление иконки в область уведомлений
- Модификация главного меню
- Контекстные всплывающие меню
- Процессы и обмен данными между ними
- Всплывающие уведомления
- Системные диалоги и запуск внешнего приложения
- Обработка глобальных горячих клавиш
- Блокировка открытия новых окон и навигации
- Предотвращение закрытия главного окна приложения
- Безопасность
- Возможности фреймворка
- Альтернативы
Electron
разработан в GitHub
и носил раньше название Atom shell
. Пожалуй, самое известное приложение, написанное с его помощью, — текстовый редактор Atom, а еще — клиент Slack для настольных компьютеров, которым очень активно пользуются в нашей редакции. Из других интересных проектов — мультипротокольный клиент мгновенных сообщений Franz, Git-клиент GitKraken, GUI-клиент к хорошо известной Node.js-разработчикам утилите Yeoman
yeoman-app и даже Microsoft Visual Studio Code.Electron
позволяет создавать кросс-платформенные приложения для настольных компьютеров с использованием чистого JavaScript
. Поддерживаются основные операционные системы: macOS, Linux, Windows. Он сочетает в себе лучшие стороны Node.js
и Chromium
, но при этом ориентирован на разработку десктопных приложений.
Само собой напрашивается сравнение Electron
с проектом Cordova
, который аналогичным образом позволяет превратить веб-приложение в мобильное приложение для основных мобильных платформ. Так когда же нам может пригодиться именно Electron
? Варианты следующие:
- если требуется кросс-платформенное приложение с малыми затратами на разработку и сопровождение;
- если есть готовое веб-приложение или веб-компонент, который необходимо распространять в виде законченного приложения для настольных компьютеров;
- если веб-приложению требуются права, выходящие за рамки ограничений системы безопасности браузеров;
- если веб-приложение должно требовать большей интеграции с операционной системой и доступа к API, невозможного из браузера.
Что будем делать
Мы возьмем готовый виджет, «обернем» его в приложение Electron
и добавим стандартные элементы интерфейса операционной системы, такие как иконка в области уведомлений, стандартные системные диалоги, вызов внешнего приложения, главное меню, горячие клавиши.
Для наших экспериментов возьмем готовый виджет платформы SoundCloud
, популярной площадки для публикации музыкальных композиций и другого аудиоконтента. Этот виджет обладает несложным API, да и можно будет немного поразвлечься прослушиванием музыки. Мы превратим его в простой проигрыватель для настольного компьютера с привычными элементами управления.
Подготовка
Иконки
Для приложения нам понадобится несколько иконок. Я брал их из набора ie_Bright с сайта iconfinder.com; можно взять другие на свой вкус. Для изображений, используемых для иконки в области уведомлений под Windows
, рекомендуются файлы .ico
, но мы для простоты возьмем только PNG-файлы.
Имя файла | Назначение | Изображение |
player.png |
Иконка приложения | |
play.png |
Начать проигрывание | |
pause.png |
Приостановить | |
prev.png |
Предыдущая композиция | |
next.png |
Следующая композиция |
Поместим иконки в подкаталог assets/img/
проекта.
Node.js
Предполагается, что на компьютере установлен Node.js версии не ниже 6.6; загрузить ее можно здесь.
$ node -v
v6.6.0
Операционная система
Примеры подготовлены для выполнения на компьютере, работающем под управлением ОС Linux и macOS.
Минимальное приложение Electron
Начнем с создания минимального приложения Electron. Для этого создадим каталог проекта, например electron-demo
, и перейдем в него:
$ mkdir electron-demo
$ cd electron-demo
Добавим в наш проект два файла — минимальный index.html
, который будет основным интерфейсом нашего приложения:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
Welcome to Electron!
</body>
</html>
и index.js
со следующим содержимым:
const path = require('path')
const electron = require('electron')
const {app, BrowserWindow} = electron
// Ссылка на объект окна; если ее не будет, окно закроется автоматически, когда сборщик мусора высвободит память из-под объекта JavaScript
let win = null
// Путь к иконкам приложения
const iconBasePath = path.join(__dirname, 'assets', 'img')
// Создание окна браузера и обработка его событий
function createWindow () {
const APP_ICON_NAME = 'player.png'
const iconPath = path.join(iconBasePath, APP_ICON_NAME)
// Задаем параметры для создания окна браузера
let options = {
width: 800, height: 550,
title: 'Electron SoundCloud Player', // Не будет работать, если в `.html` есть тег `title`
icon: iconPath,
webPreferences: {
devTools: true, // По умолчанию — `true` для показа DevTools
}
}
// Создаем окно браузера
win = new BrowserWindow(options)
// и загружаем index.html приложения
win.loadURL(`file://${__dirname}/index.html`)
// Событие во время закрытия окна
win.on('closed', (e) => {
// Это то время, когда нужно удалить соответствующий объект
// Убираем ссылку на объект окна. В многооконных приложениях окна обычно будут храниться в массиве
win = null
})
let webContents = win.webContents
// Открываем DevTools
webContents.openDevTools()
}
// Этот событие произойдет, когда Electron завершит
// инициализацию и будет готов к созданию окон браузера
// Многие методы API могут использоваться только после этого события
app.on('ready', createWindow);
// Выйти из приложения, когда все окна закрыты
app.on('window-all-closed', () => {
// На macOS приложения и их меню остаются активными до тех пор, пока
// пользователь не выйдет из них явно с помощью Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// На macOS, как правило, при клике на иконке в доке (и если нет других
// открытых окон), окно в приложении пересоздается
if (win === null) {
createWindow()
}
})
Инициализируем файл package.json
проекта менеджера пакетов npm
, ответив на необходимые вопросы.
$ npm init
Нужно проверить, что в package.json
, получившемся в результате, значение свойства main
равно main.js
(соответствует значению, введенному при запросе entry point
во время выполнения команды npm init
), в противном случае его необходимо скорректировать вручную, чтобы оно соответствовало имени главного .js
-файла проекта.
Electron
можно установить только для нашего проекта:
$ npm install --save electron
или глобально:
$ npm install -g electron
Пора запускать! Если Electron
был установлен локально, это делается следующей строкой (если он был установлен глобально, путь указывать необязательно):
$ ./node_modules/.bin/electron .
Через несколько мгновений откроется окно нашего первого приложения.
Обрати внимание, что сразу же открыто привычное окно DevTools
. Заголовок и текст окна соответствуют заданным; кроме того, у приложения есть типовое главное меню.
Виджет SoundCloud
Виджет SoundCloud
встраивается в веб-страницу как IFrame
и позволяет проигрывать отдельные композиции с сайта SoundCloud
или их списки. Он предоставляет базовый интерфейс для управления проигрыванием и разнообразную информацию о композиции.
API виджета
Методы виджета
Из методов API виджета для управления проигрыванием мы будем использовать следующие:
play
— начать проигрывание композиции;pause
— приостановить проигрывание композиции (пауза);toggle
— переключить проигрывание / приостановка;prev
— перейти к предыдущей композиции (для списка);next
— перейти к следующей композиции (для списка);bind
— добавить обработчик события виджета.
В числе прочих методов: skip
, load
, seekTo
, setVolume
, unbind
.
События виджета
События виджета делятся на две группы: аудиособытия и события пользовательского интерфейса.
Аудиособытия связаны с проигрываемой композицией и уведомляют об изменениях ее состояния в проигрывателе, передавая объект с информацией о текущей позиции в проигрываемом файле или прогрессе загрузки (relativePosition
, loadProgress
, currentPosition
).
События пользовательского интерфейса виджета уведомляют о действиях пользователя, не связанных напрямую с проигрыванием композиции.
Мы используем следующие события:
READY
— виджет загрузил данные и готов принимать внешние вызовы;PLAY
— начато проигрывание композиции;PAUSE
— проигрывание композиции приостановлено.
Остальные события: LOAD_PROGRESS
, PLAY_PROGRESS
, FINISH
, SEEK
, CLICK_DOWNLOAD
, OPEN_SHARE_PANEL
, ERROR
.
Дополнительно можно получить информацию о текущем состоянии виджета с помощью методов getVolume
, getDuration
, getPosition
, getSounds
, getCurrentSound
, getCurrentSoundIndex
, isPaused
. Информация возвращается в callback-функции. Из них нам понадобится метод getCurrentSound
, возвращающий информацию о текущей композиции.
Добавление виджета на страницу
Для того чтобы отобразить на нашей странице виджет SoundCloud
, внутри элемента <body>
добавим элемент <iframe>
, в котором загрузится сам виджет:
<!-- виджет SoundCloud -->
<iframe loading="lazy" id="sc-widget" width="100%" height="450" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/playlists/178009618&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"></iframe>
Полный список параметров виджета приведен здесь: SoundCloud Player Widget — Customize Parameters (для предыдущей версии, использующей Flash
).
Для выбора композиции или их списка и настройки визуального представления виджета можно нажать кнопку Share
на понравившемся списке композиций (если выбрана отдельная композиция, то будет невозможно перемещаться к следующей/предыдущей композиции), выбрать закладку Embed
и скопировать предлагаемый код; установив галочку More Options
, можно настроить несколько дополнительных параметров.
Можем перезапустить приложение, чтобы убедиться, что виджет действительно загрузился внутри нашего приложения.
Инициализация API виджета SoundCloud
Для доступа к API виджета необходимо добавить в тег <head>
загрузку следующего сценария:
<script src="https://w.soundcloud.com/player/api.js" type="text/javascript"></script>
Туда же добавим загрузку файла, в котором будут размещаться основные функции браузерной части приложения:
<script src="./soundcloud.js" type="text/javascript"></script>
А тег <body>
дополним кнопками управления проигрыванием:
<div>
<button id="action-widget-play">Play</button>
<button id="action-widget-pause">Pause</button>
<button id="action-widget-toggle">Toggle</button>
<button id="action-widget-prev">Prev</button>
<button id="action-widget-next">Next</button>
</div>
Создадим файл soundcloud.js
, добавив в него функцию, которая будет выполняться при загрузке окна браузера:
let initSC = function() {
const {ipcRenderer} = require('electron')
let widgetIframe = document.getElementById('sc-widget'), // IFrame виджета
widget = SC.Widget(widgetIframe) // Сам виджет
console.log('widget:', widget)
}
И собственно вызов этой функции по событию window
onload
:
// Вызвать основную функцию по событию onload объекта window
window.addEventListener('load', function load(event){
// Удалить listener, так как он больше не нужен
window.removeEventListener('load', load, false)
// Вызов основного метода
initSC()
}, false)
Теперь при запуске приложения в консоль должен быть выведен объект widget
.
Методы и события API виджета SoundCloud
Привяжем методы виджета, предназначенные для управления проигрыванием композиции, напрямую к кнопкам управления на странице (в функции initSC
):
;[ 'play', 'pause', 'toggle', 'prev', 'next' ].forEach(methodName =>
document.getElementById('action-widget-'+methodName).addEventListener('click', () =>
widget[methodName]()
)
)
Теперь кнопки на форме начнут управлять виджетом и информация о событиях будет выводиться в консоль.
Также добавим вывод в консоль уведомлений о событиях виджета:
;[ 'LOAD_PROGRESS', /* 'PLAY_PROGRESS', */ 'PLAY', 'PAUSE', 'FINISH', 'SEEK', 'READY', 'CLICK_DOWNLOAD', 'OPEN_SHARE_PANEL', 'ERROR' ].forEach(eventName =>
widget.bind(SC.Widget.Events[eventName], evt =>
console.log(`SC.Widget.Events.${eventName}`, evt)
)
)
Добавим обработчик события виджета READY
, в котором должна располагаться основная логика взаимодействия с ним. Внутри него добавим обработчик события начала проигрывания композиции PLAY
, в котором запросим и выведем информацию о текущей композиции и доступную информацию о состоянии виджета:
widget.bind(SC.Widget.Events.READY, () => {
console.log('SC.Widget.Events.READY')
widget.bind(SC.Widget.Events.PLAY, () => {
console.log('SC.Widget.Events.PLAY')
// Получить информацию о проигрываемой композиции
widget.getCurrentSound(currentSound => {
console.log('currentSound:' , currentSound)
let title = currentSound.title,
author = currentSound.user.username,
url = currentSound.permalink_url
})
// Вывести в консоль доступную информацию о текущем состоянии виджета
;[ 'getVolume', 'getDuration', 'getPosition', 'getSounds', 'getCurrentSound', 'getCurrentSoundIndex', 'isPaused' ].forEach(methodName => {
widget[methodName](value => console.log(`widget.${methodName}: `, value) )
})
})
})
Иконка в области уведомлений
Добавление иконки в область уведомлений
Одна из функций, привычных пользователю компьютера, но невозможная для веб-страницы, — это иконка с выпадающим меню в области уведомлений. Добавим в файл main.js
:
const {Menu, Tray} = electron
// Объект для вывода иконки в области уведомлений
let trayObject = null
// Инициализируем иконку по готовности приложения
app.on('ready', () => {
// Под Windows рекомендуется использовать .ico для лучшего качества
const iconName = 'pause.png'
const iconPath = path.join(iconBasePath, iconName)
// Создадим иконку в области уведомлений
trayObject = new Tray(iconPath)
// Создадим выпадающее меню
const contextMenu = Menu.buildFromTemplate([
{
label: 'Show',
click() {
win.show()
app.focus()
}
}, {
label: 'Play',
click() { console.log('contextMenu: Play') }
}, {
label: 'Pause',
click() { console.log('contextMenu: Pause') }
}, {
type: 'separator' // Разделитель
}, {
role: 'quit' // Стандартная роль пункта меню — выход из приложения
}
])
// И привяжем выпадающее меню к иконке
trayObject.setContextMenu(contextMenu)
// Зададим всплывающую подсказку
trayObject.setToolTip('This is my application.')
// Добавим обработку клика на самой иконке
trayObject.on('click', (event, bounds) => {
win.show()
app.focus()
})
})
// Уберем иконку, когда закрыты все окна
app.on('window-all-closed', () => {
if (trayObject) {
trayObject.destroy()
trayObject = null;
}
})
Под Linux отображение иконки в области уведомлений имеет свои особенности и зависит от конкретного дистрибутива. Подробнее о работе с областью уведомлений — здесь.
Модификация главного меню
Хотя мы не задавали главное меню, Electron
автоматически создал его для приложения с набором пунктов по умолчанию. Это меню можно как полностью переопределить, так и модифицировать. Для примера дополним главное меню приложения новым пунктом, добавив в main.js
:
const {MenuItem} = electron
// Когда приложение готово
app.on('ready', () => {
// Создадим объект типа MenuItem
const menuItem = new MenuItem ({
label: 'About',
click() {
dialog.showMessageBox(null, {
type: 'info',
title: 'About',
message: 'Electron SoundCloud Demo',
buttons: [ 'OK' ],
})
}
})
// Получим текущее меню приложения
let menu = Menu.getApplicationMenu();
menu.append(menuItem)
})
Контекстные всплывающие меню
Кроме главного меню приложения и меню иконки в области уведомлений, можно создавать и контекстные всплывающие меню, открывающиеся при нажатии на правую кнопку мыши на веб-странице по событию contextmenu
объекта window
.
Дополнительная информация по объектам Menu
и MenuItem
с примерами доступна по ссылкам: Menu и MenuItem.
Процессы и обмен данными между ними
Пора познакомиться с архитектурой приложения Electron
. В нем выполняется два типа процессов: Main Process
и Renderer Process
. Main Process
— главный процесс, который выполняет сценарий, указанный в поле main
файла package.json
(по умолчанию равный main.js
). Главный процесс создает объекты типа BrowserWindow
, отображающие веб-страницы, из которых строится интерфейс приложения. Каждая веб-страница выполняется в отдельном процессе, называемом Renderer Process
.
Поскольку окно браузера выполняется в Renderer Process
, а основной процесс, взаимодействующий с областью уведомлений, — в Main Process
, напрямую обращаться к одному из другого невозможно.
Один из методов организации взаимодействия между процессами в Electron
— объекты ipcMain
и ipcRenderer
, используемые в основном процессе Main Process
и процессе (или процессах) Renderer Process
, отрисовывающем веб-страницу. С помощью их методов send
и sendSync
процессы могут обмениваться синхронными и асинхронными сообщениями. Обработчику сообщения в процессе-получателе первым параметром передается объект event
с двумя свойствами, returnValue
и event.sender
. С помощью первого из них обработчик синхронного сообщения может вернуть результат отправителю; второй хранит отправителя сообщения, и асинхронный обработчик может вернуть результат, отправив сообщение с помощью event.sender.send()
.
Другой вариант организовать взаимодействие между процессами — модуль remote
, который позволяет обмениваться данными в стиле вызовов RPC, вызывая методы другого процесса. В main.js
, в описании выпадающего меню иконки области уведомлений (вызов метода Menu.buildFromTemplate()
), изменим обработчики нажатия на пункты меню с вывода в консоль на отправку сообщений следующим образом:
...
}, {
label: 'Play',
// click() { console.log('contextMenu: Play') }
click() { win && win.send('do-play') }
}, {
label: 'Pause',
// click() { console.log('contextMenu: Pause') }
click() { win && win.send('do-pause') }
}, {
...
В файле soundcloud.js
подключим объекты ipcRenderer
из модуля electron
первой строкой внутри основной функции initSC
:
const { ipcRenderer } = require('electron')
И добавим в этом же файле, в конце обработчика события SC.Widget.Events.READY
, обработчики этих событий:
// При получении сообщений от главного процесса вызвать соответствующий метод виджета
ipcRenderer.on('do-play', () => widget.play() )
ipcRenderer.on('do-pause', () => widget.pause() )
Теперь при получении сообщений do-play
и do-pause
будут вызываться соответствующие методы виджета и мы можем управлять виджетом из области уведомлений.
Добавим передачу уведомлений в обратную сторону, о событиях виджета. Добавим в конце callback-функции widget.getCurrentSound
строку, которая отправит сообщение sc-play
главному процессу в момент начала новой композиции с информацией о ее авторе и названии:
ipcRenderer.send('sc-play', author, title)
В конце обработчика события SC.Widget.Events.READY
добавим обработку события SC.Widget.Events.PAUSE
, при его получении уведомив об этом главный процесс сообщением sc-pause
.
widget.bind(SC.Widget.Events.PAUSE, (evt) => {
// Отправить IPC-сообщение главному процессу о приостановке проигрывания
ipcRenderer.send('sc-pause')
});
В main.js
добавим функцию, изменяющую иконку для области уведомлений:
function setTrayImage(iconName) {
const iconPath = path.join(iconBasePath, iconName)
// Установим иконку
trayObject.setImage(iconPath)
}
и обработчики наших событий sc-play
и sc-pause
:
const { ipcMain } = electron
ipcMain.on('sc-play', (event, author, title) => {
console.log(`sc-play: author: ${author}, title: ${title}`)
// Сменим изображение
setTrayImage('play.png')
// Зададим всплывающую подсказку
trayObject.setToolTip(`<div>${author} - ${title}</div>`)
})
ipcMain.on('sc-pause', (event) => {
console.log('sc-pause')
// Сменим изображение
setTrayImage('pause.png')
// Зададим всплывающую подсказку
trayObject.setToolTip('This is my application.')
})
Всплывающие уведомления
Создание всплывающих уведомлений в Electron
реализуется через обычный Notifications API
браузера. Единственное отличие — у пользователя не будет запрашиваться разрешение о выводе уведомлений.
Для отображения уведомления необходимо создать объект типа Notification
, вызвав его конструктор с текстом заголовка и параметрами, в числе которых иконка, текст уведомления и флаг разрешения звукового оповещения при отображении уведомления. Так как мы проигрываем музыкальную композицию, звук нужно будет отключить. Создадим файл notification.js
, который будет выводить само уведомление и вызывать callback
при клике на уведомлении:
const NOTIFICATION_ICON_PATH = './assets/img/player.png';
const NOTIFICATION_TIMEOUT = 3000
var spawnNotification = function(title, body, callback) {
// Обезопасим callback
callback = typeof callback === 'function' ? callback : function() {};
// Определяем параметры уведомления
let options = {
body: body, // Текст уведомления
icon: NOTIFICATION_ICON_PATH, // Путь к файлу с иконкой
silent: true // Запрет звукового оповещения
}
// Создаем само уведомление
let n = new Notification(title, options);
// Задаем обработчик клика на уведомлении
n.onclick = () => {
console.log('Notification clicked')
callback();
}
// Устанавливаем тайм-аут для скрытия уведомления
setTimeout(n.close.bind(n), NOTIFICATION_TIMEOUT);
}
module.exports = { spawnNotification }
Подключим этот модуль, добавив в первые строки функции initSC
файла soundcloud.js
:
const { spawnNotification } = require('./notification')
И вызов этой функции внутри обработчика события SC.Widget.Events.PLAY
виджета; ее callback передаст сообщение sc-open
главному процессу, уведомив его о том, что пользователь кликнул на уведомлении:
// Показать уведомление с автором и названием композиции
spawnNotification(author, title, () => {
// Callback вызывается, если пользователь кликнул на уведомлении
// Уведомим главный процесс, передав URL с информацией о композиции
ipcRenderer.send('sc-open', url)
});
Системные диалоги и запуск внешнего приложения
В основном процессе при нажатии на уведомление мы будем открывать в браузере по умолчанию страницу с текущей композицией.
Для этого используем функцию shell.openExternal()
. Она выполняет действие по умолчанию для типа данных, переданных в качестве параметра. Если этой функции будет передан URL, откроется окно браузера по умолчанию, который загрузит страницу, соответствующую этому URL.
Необходимо отметить, что источником данных в этом случае служит сторонний виджет, расположенный на стороннем сайте, и нельзя полагаться на то, что в поле permalink_url
текущей композиции будет именно URL, а не, например, скрипт с командой на удаление всего содержимого диска.
Для демонстрационного примера мы ограничимся запросом пользователя на разрешение выполнения данного действия. Отображение системных диалогов реализуется с помощью метода showMessageBox()
объекта dialog
. Всего доступно четыре диалоговых окна; кроме showMessageBox()
, есть еще showOpenDialog
и showSaveDialog
для стандартных диалоговых окон открытия и сохранения файла и showErrorBox
. Документация по объекту dialog
приведена здесь.
Добавим в main.js
обработчик сообщения sc-open
, отправляемого окном браузера при клике пользователя на уведомлении:
const { shell } = electron
ipcMain.on('sc-open', (event, url) => {
let options = {
type: 'question',
message: `Application requested to execute the external resource: ${url}. This may be unsafe. Are you sure?`,
buttons: [ 'OK', 'Cancel' ], // Кнопки нумеруются с нуля: 0, 1
defaultId: 1, // Индекс кнопки по умолчанию ('Cancel')
};
// Покажем пользователю диалог
dialog.showMessageBox(null, options, (buttonId) => {
// `buttonId` — индекс нажатой пользователем кнопки (или `defaultId`)
if (buttonId === 0) shell.openExternal(url) // Выполним действие по умолчанию
})
})
Обработка глобальных горячих клавиш
Зарегистрируем горячую клавишу Control + Shift + P
(Command + Shift + P
под macOS
) для начала или приостановки проигрывания композиции.
Добавим в main.js
:
const {globalShortcut} = require('electron')
app.on('ready', () => {
// Зарегистрировать глобальный обработчик сочетания горячих клавиш 'CommandOrControl+Shift+P'
globalShortcut.register('CommandOrControl+Shift+P', () => {
console.log('CommandOrControl+Shift+P')
win && win.send('do-toggle')
})
// Зарегистрируем глобальный обработчик горячей клавиши управления медиапроигрывателем `MediaPlayPause`
globalShortcut.register('MediaPlayPause', () => {
console.log('MediaPlayPause')
win && win.send('do-toggle')
})
})
Модификатор CommandOrControl
соответствует клавише Command
под macOS
и Control
под Linux
и Windows
.
Добавим в файл soundcloud.js
рядом с обработчиками сообщений do-play
и do-pause
вызов метода виджета widget.toggle()
при получении события do-toggle
(обработчик события SC.Widget.Events.READY
):
ipcRenderer.on('do-toggle', () => widget.toggle() )
Полный список обозначений клавиш и модификаторов приведен в документации здесь.
Блокировка открытия новых окон и навигации
Виджет SoundCloud
содержит ссылки на другие страницы сайта. Наше приложение однооконное, и открытие других окон при клике на ссылку нежелательно. При навигации на другую страницу в том же окне, в отличие от браузера, интерфейс нашего приложения не позволяет вернуться назад.
Ограничим действия пользователя — заблокируем открытие нового окна и навигацию, вызвав для этого метод событий preventDefault()
внутри обработчиков событий new-window
и will-navigate
объекта win.webContent
следующим образом (в конце функции createWindow
файла main.js
, под объявлением переменной webContents
):
// Заблокировать открытие нового окна
webContents.on('new-window', (event) => {
event.preventDefault()
})
// Заблокировать навигацию на другие веб-страницы
webContents.on('will-navigate', (event) => {
event.preventDefault()
})
Предотвращение закрытия главного окна приложения
Кроме события closed
, вызываемого при закрытии окна, у окна есть событие close
, которое вызывается перед закрытием окна, и, если вызвать его метод preventDefault()
, окно закрыто не будет.
Добавим в начале файла main.js
переменную preventClose
— флаг, определяющий, можно ли закрывать главное окно приложения:
// По умолчанию предотвращать закрытие приложения
let preventClose = true;
Добавим внутри функции createWindow
файла main.js
блокировку закрытия окна, если установлен флаг preventClose
:
// Событие перед закрытием окна
win.on('close', (e) => {
if (preventClose) {
// Блокируем действие по умолчанию — закрытие окна
e.preventDefault()
// Скрываем окно
win.hide()
}
})
И в конце файла main.js
определим обработчик события app.on('before-quit')
, вызываемого при попытке завершить приложение в целом (в нашем случае будет вызвано при выборе пункта меню Quit
):
app.on('before-quit', () => preventClose = false)
Безопасность
Мы уже затрагивали вопросы безопасности, когда открывали в браузере URL, полученный с сервера, выполняя для него действие по умолчанию. Каждый раз, когда код, полученный с удаленного ресурса, выполняется локально, существует риск злонамеренного доступа к локальным ресурсам. В связи с этим настоятельно советуем почитать замечания о безопасности в Electron.
Возможности фреймворка
В числе других возможностей интеграции с интерфейсом операционной системы: список последних документов (Windows и macOS), меню приложения для док-панели macOS, объявление списка пользовательских задач приложения в Windows, миниатюры списка задач Windows, добавление ссылок в панель запуска Unity (Linux), индикатор хода выполнения, накладной значок и подсветка кнопки приложения на панели задач Windows, перетаскивание файлов из окна Electron
.
Среди системных возможностей — доступ к информации об изменении состоянии монитора питания, блокировка перехода в спящий режим, получение информации об экране и изображения на экране, получение уведомлений об изменении статуса сетевого подключения (онлайн/офлайн) и многое другое.
Альтернативы
Конкурирует с Electron
проект NW.js, ранее носивший название node-webkit
.
INFO
Для подготовки дистрибутива можно использовать проект electron-packager, позволяющий собрать исполняемые файлы и исходный код приложения в единый пакет. Для обхода ограничений на длину имен файлов и ускорения загрузки модулей
Electron
поддерживает упаковку файлов приложения в пакет с расширением .asar
, содержимое которого доступно из самого приложения с использованием стандартного файлового API.
Electron
обладает встроенным механизмом автоматических обновлений autoUpdater. Реализован и механизм автоматической отправки отчетов о падениях приложения на удаленный сервер с помощью объекта crashReporter.