Задавались ли вы когда-нибудь вопросом, возможно ли создавать кроссплатформенные настольные приложения на HTML, CSS и JavaScript? С Electron это становится возможным. В этой статье мы рассмотрим основы Electron и напишем простое приложение.
Функциональность нашего приложения будет заключаться в том, что при нажатии определённой клавиши на клавиатуре будет воспроизводиться соответствующий звук.
Прим. перев. Для создания приложений с использованием Electron не требуется знание таких языков программирования, как C++ и Python, — знания веб-технологий будет достаточно. Если вы не ограничены веб-технологиями, хорошей альтернативой использованию Electron будут GTK+ и Qt: в отличие от «родных» приложений, приложения, написанные с использованием Electron, обладают множеством недостатков, в том числе крайне неэффективно используют свободную оперативную память компьютера. Статью по QT можно посмотреть у нас на сайте.
Electron — фреймворк для кроссплатформенной разработки настольных приложений с использованием Chromium и Node.js.
С его помощью можно легко написать приложение с использованием HTML, CSS и JavaScript, которое «из коробки» будет совместимо с Mac, Windows и Linux.
Другие встроенные особенности включают:
- Автоматические обновления приложений;
- Нативные меню и уведомления;
- Сообщения об ошибках, которые можно отправлять на удалённый сервер;
- Отладка и профилирование — модуль content Chromium ищет места, где проседает производительность. Вы также можете использовать инструменты разработчика в своём приложении;
- Быстрое и простое создание установочных пакетов для Windows.
Если вы довольны тем, что предлагает Electron, то давайте займёмся нашим приложением. Однако прежде чем мы начнём, необходимо установить Node.js. Также вам пригодится аккаунт на GitHub, чтобы хранить и обновлять своё приложение. Это делать необязательно, но желательно, так как в наше время важно знать, как работает GitHub.
Принимаемся за работу
Когда вы закончите с подготовкой, откройте терминал и следуйте дальнейшим инструкциям, чтобы клонировать репозиторий Electron Quick Start на ваш компьютер. Именно на основе Electron Quick Start мы и будем писать наше приложение.
# Клонируем репозиторий
git clone https://github.com/electron/electron-quick-start
# Переходим к нему
cd electron-quick-start
# Устанавливаем зависимости и запускаем
npm install && npm start
После выполнения этих шагов приложение должно запуститься в чём-то, похожем на окно браузера. Хотя, это и есть окно браузера!
Как было сказано ранее, в своём приложении вы можете использовать инструменты разработчика. Всё, что вы можете делать с инструментами вашего браузера, вы также можете делать и в приложении. Потрясающе!
Архитектура приложения
Теперь давайте взглянем на код и структуру приложения.
Структура самая обычная, очень похожая на ту, что используется при создании веб-страниц. У нас есть:
index.html
— HTML-страница, отвечает за внешний вид;main.js
— создаёт окна и управляет системными событиями;package.json
— описание пакета (имя, зависимости и т.д.) для npm;renderer.js
— управляет процессами рендеринга.
Возможно, вы задаётесь вопросом: «Что за звери эти процессы рендеринга и основной процесс?» Давайте разбираться.
Что есть процесс?
Когда вы видите слово «процесс», думайте о процессе в операционной системе. По сути, это экземпляр программы, работающей в системе.
Если запустить наше Electron-приложение и заглянуть в Диспетчер Задач в Windows, Мониторинг Активности в macOS или Системный Монитор в Linux, то можно увидеть процессы, связанные с приложением.
Все они работают параллельно, однако память и ресурсы, выделенные под каждый из них, изолированы от других процессов.
Допустим, мы хотим написать цикл в процессе рендеринга:
var a = 1;
for ( a = 1; a < 10; a ++) {
console.log('Это цикл for');
}
Этот код никак не повлияет на основной процесс.
Основной процесс
Этот процесс контролирует происходящее в приложении. В него встроен полноценный Node.js API. Из него создаются процессы рендеринга и открываются диалоговые окна. Также он отвечает за разное взаимодействие с операционной системой, запускает и закрывает приложение.
Файл с этим процессом принято называть main.js
, но вы можете дать ему любое имя. Также вы можете менять файл основного процесса через package.json
. Чтобы проверить, как это работает, откройте файл package.json
, замените строку "main": "main.js"
на "main": "mainTest.js"
и попробуйте запустить приложение.
Имейте в виду, что основной процесс может быть только один.
Процесс рендеринга
Этот процесс представляет собой окно браузера в вашем приложении. В отличие от основного процесса, процессов рендеринга может быть несколько и каждый из них будет независим от остальных. За счёт этого ошибка в одном из них никак не повлияет на другие. Скажем за это спасибо многопроцессорной архитектуре Chromium. Также эти окна можно спрятать или изменить, так как они работают как HTML-файлы.
Но в Electron у нас также есть доступ к Node.js API. Это значит, что мы можем открывать диалоговые окна и взаимодействовать с операционной системой прочими способами.
Представить происходящее можно следующим образом:
Остаётся один вопрос. Можно ли как-нибудь связать эти процессы?
Эти процессы выполняются одновременно и независимо. Однако им всё равно нужно как-то взаимодействовать. Особенно учитывая то, что они отвечают за разные задачи.
Специально для таких целей существует межпроцессное взаимодействие (IPC). Его можно использовать для передачи сообщений между основным процессом и процессами рендеринга.
Вот мы и разобрались с основами процессов для создания Electron-приложения. Возвращаемся к коду!
Добавим индивидуальности
Поменяем название папки с нашим приложением на более подходящее. Измените название папки с electron-quick-start
на hear-me-type-tutorial
. Откройте папку заново в текстовом редакторе или IDE. Теперь похимичим с файлом package.json
. Он содержит важную информацию о нашем приложении: имя, версию, автора, лицензию и многое другое.
Давайте укажем, кто является автором приложения. Для этого найдите параметр author
и замените его значение на своё имя. Должно получиться что-то такое: "author": "Carol Pelu"
. Также вы можете изменить и другие параметры вроде name
и description
, которые отвечают за название приложения и его описание соответственно. В итоге должно получиться примерно так:
Помните, что вы всегда можете ввести npm start
в терминале, чтобы запустить приложение и посмотреть на внесённые изменения.
Пора идти дальше и добавить в наше приложение функциональность.
Добавляем функциональность
Мы хотим, чтобы при нажатии клавиши на клавиатуре проигрывался соответствующий звук. Чтобы реагировать на пользовательский ввод, мы должны определить элемент, который будет перехватывать нажатия клавиш и затем активировать нужное действие.
Для этого мы создадим элементы audio
со своим id
для каждой клавиши. Затем напишем switch-конструкцию, чтобы понять, какая клавиша была нажата. После этого воспроизведём звук, привязанный к этой клавише. Если это звучит сложно — не беспокойтесь, мы разберёмся со всем пошагово.
Скачайте этот архив с нужными нам аудиофайлами. Пора встраивать аудио в наше приложение.
Откройте index.html
и внутри <body>
создайте новый элемент <div>
с классом audio
.
Затем внутри этого <div>
создайте элемент <audio>
с id
равным "A"
, src
равным "sounds/A.mp3"
и атрибутом preload
равным "auto"
.
Мы используем preload="auto"
, чтобы сказать приложению, что оно должно загрузить весь аудиофайл после загрузки страницы. Главным файлом нашего приложения является index.html
, и все аудио загрузятся после запуска приложения.
В итоге код должен выглядеть так:
<div class="audio">
<audio id="A" src="sounds/A.mp3" preload="auto"></audio>
</div>
Теперь index.html
имеет примерно такой вид:
Сейчас <audio>
указывает на неизвестный файл. Давайте создадим папку sounds
и поместим туда все аудиофайлы.
Отлично! Теперь нам не хватает только JavaScript-кода.
Создадим новый файл functions.js
. Давайте запросим его в файле index.html
, чтобы JS-код был готов к использованию, когда приложение будет запущено.
Следуя примеру require(./renderer.js')
, добавим строку require('./functions.js')
прямо под ней.
Проект теперь должен иметь такой вид:
Отлично! Теперь, когда уже почти всё готово, наступает момент истины.
Откроем functions.js
и добавим туда следующий код:
document.onkeydown = function(e) {
switch (e.keyCode) {
case 65:
document.getElementById('A').play();
break;
default:
console.log("Клавиша на обнаружена!");
}
};
Откройте консоль, убедитесь, что вы находитесь в директории проекта и введите npm start
для запуска приложения.
Сделайте звук погромче и нажмите клавишу «А» на клавиатуре.
#крышесносно
JS-код довольно простой. Мы используем событие onkeydown
для объекта document
, чтобы выяснить, к какому HTML-элементу мы обращаемся. Имейте в виду, что объектом document
является главное окно нашего приложения.
В анонимной функции мы используем switch-выражение, которое выясняет Unicode-значение нажатой клавиши. Если это значение правильное, то воспроизводится звук. В противном случае в консоль выводится сообщение: «Клавиша не обнаружена!».
Как вы могли заметить, у нас есть файлы для клавиш от A до Z и от 0 до 9. Поэтому давайте используем и их, чтобы «А» было не так одиноко.
Вернёмся к index.html
и создадим элемент <audio>
для всех клавиш, к которым у нас есть аудио. Да, можете просто скопипастить:
<audio id="B" src="sounds/B.mp3" preload="auto"></audio>
<audio id="C" src="sounds/C.mp3" preload="auto"></audio>
<audio id="D" src="sounds/D.mp3" preload="auto"></audio>
<audio id="E" src="sounds/E.mp3" preload="auto"></audio>
<audio id="F" src="sounds/F.mp3" preload="auto"></audio>
<audio id="G" src="sounds/G.mp3" preload="auto"></audio>
<audio id="H" src="sounds/H.mp3" preload="auto"></audio>
<audio id="I" src="sounds/I.mp3" preload="auto"></audio>
<audio id="J" src="sounds/J.mp3" preload="auto"></audio>
<audio id="K" src="sounds/K.mp3" preload="auto"></audio>
<audio id="L" src="sounds/L.mp3" preload="auto"></audio>
<audio id="M" src="sounds/M.mp3" preload="auto"></audio>
<audio id="N" src="sounds/N.mp3" preload="auto"></audio>
<audio id="O" src="sounds/O.mp3" preload="auto"></audio>
<audio id="P" src="sounds/P.mp3" preload="auto"></audio>
<audio id="Q" src="sounds/Q.mp3" preload="auto"></audio>
<audio id="R" src="sounds/R.mp3" preload="auto"></audio>
<audio id="S" src="sounds/S.mp3" preload="auto"></audio>
<audio id="T" src="sounds/T.mp3" preload="auto"></audio>
<audio id="U" src="sounds/U.mp3" preload="auto"></audio>
<audio id="V" src="sounds/V.mp3" preload="auto"></audio>
<audio id="W" src="sounds/W.mp3" preload="auto"></audio>
<audio id="X" src="sounds/X.mp3" preload="auto"></audio>
<audio id="Y" src="sounds/Y.mp3" preload="auto"></audio>
<audio id="Z" src="sounds/Z.mp3" preload="auto"></audio>
<audio id="0" src="sounds/0.mp3" preload="auto"></audio>
<audio id="1" src="sounds/1.mp3" preload="auto"></audio>
<audio id="2" src="sounds/2.mp3" preload="auto"></audio>
<audio id="3" src="sounds/3.mp3" preload="auto"></audio>
<audio id="4" src="sounds/4.mp3" preload="auto"></audio>
<audio id="5" src="sounds/5.mp3" preload="auto"></audio>
<audio id="6" src="sounds/6.mp3" preload="auto"></audio>
<audio id="7" src="sounds/7.mp3" preload="auto"></audio>
<audio id="8" src="sounds/8.mp3" preload="auto"></audio>
<audio id="9" src="sounds/9.mp3" preload="auto"></audio>
Потрясающе! Теперь давайте провернём то же самое в functions.js
.
Код для каждой клавиши можно найти здесь. Но вы по-прежнему можете просто скопировать:
document.onkeydown = function(e) {
switch (e.keyCode) {
case 48:
document.getElementById('0').play();
break;
case 49:
document.getElementById('1').play();
break;
case 50:
document.getElementById('2').play();
break;
case 51:
document.getElementById('3').play();
break;
case 52:
document.getElementById('4').play();
break;
case 53:
document.getElementById('5').play();
break;
case 54:
document.getElementById('6').play();
break;
case 55:
document.getElementById('7').play();
break;
case 56:
document.getElementById('8').play();
break;
case 57:
document.getElementById('9').play();
break;
case 65:
document.getElementById('A').play();
break;
case 66:
document.getElementById('B').play();
break;
case 67:
document.getElementById('C').play();
break;
case 68:
document.getElementById('D').play();
break;
case 69:
document.getElementById('E').play();
break;
case 70:
document.getElementById('F').play();
break;
case 71:
document.getElementById('G').play();
break;
case 72:
document.getElementById('H').play();
break;
case 73:
document.getElementById('I').play();
break;
case 74:
document.getElementById('J').play();
break;
case 75:
document.getElementById('K').play();
break;
case 76:
document.getElementById('L').play();
break;
case 77:
document.getElementById('M').play();
break;
case 78:
document.getElementById('N').play();
break;
case 79:
document.getElementById('O').play();
break;
case 80:
document.getElementById('P').play();
break;
case 81:
document.getElementById('Q').play();
break;
case 82:
document.getElementById('R').play();
break;
case 83:
document.getElementById('S').play();
break;
case 84:
document.getElementById('T').play();
break;
case 85:
document.getElementById('U').play();
break;
case 86:
document.getElementById('V').play();
break;
case 87:
document.getElementById('W').play();
break;
case 88:
document.getElementById('X').play();
break;
case 89:
document.getElementById('Y').play();
break;
case 90:
document.getElementById('Z').play();
break;
default:
console.log("Key is not found!");
}
};
Прим. перев. Как вы, вероятно, заметили, такая switch-case конструкция выглядит довольно громоздко. А как вы бы оптимизировали этот участок кода? Делитесь своими вариантами в комментариях.
Вот мы и закончили наше приложение! Поздравляем!
Основная функциональность в приложении присутствует, но его ещё можно доработать.
Дополняем приложение
Да, у нас всё работает, но всё равно то тут, то там чего-то не хватает. Например, в index.html
вы можете изменить заголовок приложения и содержимое основного окна. Кроме того, у нас нет никакого дизайна, нет красивых цветов и нет картинок с котиками. Включите воображение и попробуйте улучшить внешний вид приложения.
Код тоже не верх совершенства. У нас куча одинакового кода, который можно оптимизировать и улучшить. В итоге код будет занимать меньше места, и глазам будет не так больно. Помните: повторяющийся код — это плохо.
Тестируем, тестируем и ещё раз тестируем
Хорошее ПО должно быть тщательно протестировано. Попробуйте нажать каждую клавишу, чтобы увидеть, что произойдёт. В лучшем случае вы услышите звук для каждой клавиши, указанной в коде. Но что если вы нажмёте много клавиш подряд так быстро, как только можете? А что насчёт клавиш вроде Home и NumLock, для которых у нас нет звука?
Если вы свернёте приложение и нажмёте клавишу, вы услышите звук? А если окно приложения неактивно, и вы нажмёте клавишу, то что-нибудь произойдёт?
К сожалению, ответ — нет.
Так происходит из-за архитектуры, на которой построен Electron. Вы можете регистрировать нажатия клавиш внутри приложения как в C#, но не можете этого делать за его пределами. Это выходит за рамки привычных Electron-приложений.
Пройдитесь по коду строка за строкой и попробуйте сделать его нерабочим. Посмотрите, что произойдёт и какие ошибки выбросит Electron. Это упражнение поможет вам разобраться в отладке. Если вы знаете слабые места своего приложения, то вы знаете, как их исправить и сделать приложение лучше.
В файле functions.js
было использовано устаревшее событие. Сможете его найти? Когда найдёте, подумайте, как его заменить без изменения функциональности приложения.
Использование устаревшего кода — плохая практика. Это может привести к серьёзным багам, о существовании которых вы могли даже не подозревать. Следите за документацией языка, чтобы знать, какие произошли изменения. Всегда будьте в курсе последних событий.
Перевод статьи «How to Build Your First Desktop App with JavaScript Using Electron»
Данный материал является вольным переводом статьи:
Danny Markov Creating Your First Desktop App With HTML, JS and Electron
Материал вычитывал: Михаил Синяков
Веб-приложения становятся все более мощными с каждым годом, но остается еще место для классических приложений, обладающих полным доступом к оборудованию компьютера. Сегодня вы можете создать десктопное приложения при помощи хорошо знакомых вам HTML, JS и Node.js, упаковать его в исполняемый файл и пользоваться им на Windows, OS X и Linux.
Существуют два самых популярных проекта с открытым исходным кодом, позволяющих сделать это. Это NW.js и Electron, последний мы и будем рассматривать сегодня. Мы собираемся переписать версию, которую делали на NW.js, так что вы сможете еще и сравнить их между собой.
Программы, которые создаются при помощи Electron это просто веб сайты, которые открываются во встроенном браузере Chromium. В дополнение к стандартным API HTML5 эти сайты могут использовать полный набор модулей Node.js и специальных модулей Electron, которые позволяют получить доступ к операционной системе.
В этом уроке мы создадим простое приложение, которое получает последние статьи с сайта Tutorialzine через RSS и отображает их в виде карусели. Все исходники, вы можете скачать архивом по ссылке.
Распакуйте его содержимое в любую директорию на вашем компьютере.
Глядя на структуру файлов вы никогда бы не догадались что это десктопное приложение, а не просто веб сайт.
Мы рассмотрим наиболее интересные файлы и то как они работают минутой позже, а пока давайте заглянем под капот.
Запуск приложения
Поскольку приложение Electron это просто Node.js приложение, вам нужно установить npm. Сделать это довольно просто.
Откройте терминал и выполните в директории проекта следующую команду:
npm install
Это создаст папку node_modules, содержащую все необходимые зависимости для приложения. Затем, введите в терминале следующее:
npm start
Приложение должно открыться в новом окне, обратите внимание, что оно имеет только верхнее меню и больше ничего.
Вы наверное обратили внимание,что приложение запускается не слишком удобно для пользователя. Однако это просто способ для разработчика запустить приложение. Когда оно будет упаковано, пользователь будет запускать его как обычно — двойным кликом по иконке.
Как это сделано
Сейчас мы поговорим о наиболее важных файлах, которые используются в любом приложении, написанном при помощи Electron. Давайте начнем с файла package.json, который содержит различную информацию о проекте. Например, версию, список npm зависимостей и другую не менее важную информацию.
package.json
{
"name": "electron-app",
"version": "1.0.0",
"description": "",
"main": "main.js",
"dependencies": {
"pretty-bytes": "^2.0.1"
},
"devDependencies": {
"electron-prebuilt": "^0.35.2"
},
"scripts": {
"start": "electron main.js"
},
"author": "",
"license": "ISC"
}
Если вы уже работали с Node.js, то у вас уже имеется представление как это все работает. Важно отметить команду npm start
которая запускает приложение. Когда мы вызываем эту команду в консоли, то просим electron запустить файл main.js. Этот файл содержит маленький скрипт, который открывает окно приложения, определяет некоторые параметры и обработчики событий.
main.js
var app = require('app'); // Модуль управления приложением.
var BrowserWindow = require('browser-window'); // Модуль для создания окна браузера.
// Сохраняем глобальную ссылку на объект Window, если этого не сделать
// окно закроется автоматически как только сработает сборщик мусора JS.
var mainWindow = null;
// Выйти, после того как все окна будут закрыты.
app.on('window-all-closed', function() {
// В OS X это характерно для приложений и их меню,
// чтобы оставаться активными, пока пользователь явно не завершит работу
// при помощи Cmd + Q
if (process.platform != 'darwin') {
app.quit();
}
});
// Этот метод будет вызван когда Electron закончил
// инициализацию и готов к созданию окна браузера.
app.on('ready', function() {
// Создаем окно браузера.
mainWindow = new BrowserWindow({width: 900, height: 600});
// и загружаем index.html в приложение.
mainWindow.loadURL('file://' + __dirname + '/index.html');
// Генерируется когда окно закрыто.
mainWindow.on('closed', function() {
// Сброс объекта окна, обычно нужен когда вы храните окна
// в массиве, это нужно если в вашем приложении множество окон,
// в таком случае вы должны удалить соответствующий элемент.
mainWindow = null;
});
});
Давайте взглянем на то, что мы делаем в методе ready
. Сначала мы определяем окно браузера и устанавливаем его первоначальный размер. Затем мы загружаем в него файл index.html, который работает точно так же, как если бы мы открыли его в браузере.
Как вы видите, в самом файле нет ничего особенного — контейнер для карусели и пункты для отображения статистики использования процессора и оперативной памяти.
index.html
<!DOCTYPE html>
<html>
<head> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tutorialzine Electron Experiment</title>
<link rel="stylesheet" href="./css/jquery.flipster.min.css">
<link rel="stylesheet" href="./css/styles.css">
</head>
<body>
<div class="flipster">
<ul>
</ul>
</div>
<p class="stats"></p>
<!-- Правильный способ подключить jQuery в Electron приложении -->
<script>window.$ = window.jQuery = require('./js/jquery.min.js');</script>
<script src="./js/jquery.flipster.min.js"></script>
<script src="./js/script.js"></script>
</body>
</html>
Здесь у нас html-код, ссылки на необходимые стили, js библиотеки и скрипты. Заметили что jQuery подключен странным способом? См. этот issue, чтобы узнать почему подключение происходит именно так.
Наконец, собственно сам JavaScript код нашего приложения. В нем мы подключаемся к RSS ленте, получаем последние статьи и показываем их. Если мы попытаемся провернуть такую операцию в окружении браузера, то ничего не получится. Канал находится на другом домене и получение данных с него запрещено. Однако в Electron такого ограничения нет, мы можем получить необходимую информацию при помощи AJAX-запроса.
$(function(){
// Отображаем информацию о компьютере используя node-модуль os.
var os = require('os');
var prettyBytes = require('pretty-bytes');
$('.stats').append('Number of cpu cores: <span>' + os.cpus().length + '</span>');
$('.stats').append('Free memory: <span>' + prettyBytes(os.freemem())+ '</span>');
// Библиотека UI компонентов Electron. Понадобится нам позже.
var shell = require('shell');
// Получаем последние записи с Tutorialzine.
var ul = $('.flipster ul');
// Политики безопасности в Electron не применяются, поэтому
// мы можем отправлять ajax-запросы на другие сайты. Обратимся к Tutorialzine
$.get('http://feeds.feedburner.com/Tutorialzine', function(response){
var rss = $(response);
// Найдем все статьи в RSS потоке:
rss.find('item').each(function(){
var item = $(this);
var content = item.find('encoded').html().split('</a></div>')[0]+'</a></div>';
var urlRegex = /(http|ftp|https)://[w-_]+(.[w-_]+)+([w-.,@?^=%&:/~+#]*[w-@?^=%&/~+#])?/g;
// Получим первое изображение из статьи.
var imageSource = content.match(urlRegex)[1];
// Создадим li для каждой статьи и добавим в неупорядоченный список.
var li = $('<li><img /><a target="_blank"></a></li>');
li.find('a')
.attr('href', item.find('link').text())
.text(item.find("title").text());
li.find('img').attr('src', imageSource);
li.appendTo(ul);
});
// Инициализируем плагин flipster.
$('.flipster').flipster({
style: 'carousel'
});
// При клике на статью откроем страницу в браузере по умолчанию.
// В противном случае откроем ее в окне Electron.
$('.flipster').on('click', 'a', function (e) {
e.preventDefault();
// Откроем URL в браузере по умолчанию.
shell.openExternal(e.target.href);
});
});
});
Есть одна классная вещь, в приведенном выше коде, она заключается в том, что в одном файле можно одновременно использовать:
- JavaScript библиотеки — jQuery и jQuery Flipster для создания карусели.
- Собственный модули Electron — оболочку, которая предоставляет API для desktop-задач. В нашем случае открытие url в браузере по умолчанию.
- Node.js модули — Модуль OS для доступа к информации о системе, Pretty Bytes для форматирования.
С их помощью наше приложение готово к работе!
Упаковка и дистрибуция.
Есть еще одна важная вещь, которую нужно сделать чтобы ваше приложение попало к конечному пользователю. Вы должны упаковать его в исполняемый файл, который будут запускать двойным щелчком. Необходимо будет собрать отдельный дистрибутив для каждой из систем: Windows, OS X, Linux. В этом нам поможет Electron Packager.
Вы должны принять во внимание тот факт, что в упакованный файл попадут все ваши ресурсы, все зависимости node.js, а так же уменьшенная копия браузера webkit. В конечном итоге получится файл порядка 50mb. Это довольно много и не практично для простых приложений, как, например, наше, но этот вопрос становится не актуальным, когда речь идет о больших и сложных приложениях.
Заключение
Единственное серьезное отличие от NW.js состоит в том, что в NW.js точкой входа выступает HTML-файл, в то время как в Electron эту роль выполняет JavaScript файл. C Electron вы получаете больше контроля. Вы легко можете построить мульти оконное приложение и организовать обмен данными между ними.
Вот что можно еще почитать по теме:
- Electron’s Quick Start Guide
- Electron’s Documentation
- Apps Built with Electron
Хочешь проверить свои знания по JS?
Подпишись на наш канал с тестами по JS в Telegram!
Решать задачи
×
Перевод статьи Бианки Плющевской «5 Best JavaScript Frameworks for Desktop Apps».
Еще не так давно построить десктопное приложение с помощью JavaScript было невозможно. К счастью, те времена прошли, и теперь JS-разработчики могут применить свои знания и опыт в веб-разработке для создания десктопных приложений.
Как всегда, легче сказать, чем сделать. Занимаясь поиском материала, мы заметили, что существует много путаницы относительно того, как на самом деле построены десктопные приложения на JavaScript, и того, как они работают. Также довольно сложно подобрать правильные инструменты для работы над подобным проектом.
В этой статье мы поближе познакомимся с пятью самыми известными JavaScript-фреймворками для десктопных приложений.
#1 Electron
Electron это фреймворк с открытым исходным кодом. Изначально он был создан GitHub для редактора Atom, и произошло это в 2013 году. Эта библиотека позволяет вам создавать GUI десктопных приложений с помощью таких веб-технологий как JavaScript, HTML и CSS.
Десктопные приложения, созданные с помощью Electron, ведут себя, как веб-приложения, но могут читать и записывать данные в файловой системе компьютера. На рынке есть много десктопных приложений, построенных на Electron. Например, Skype для Linux или Slack. Если хотите узнать больше о популярных десктопных приложениях, построенных с помощью Electron, почитайте эту статью.
Существенное преимущество этого решения в том, что веб-разработчику не приходится изучать новую технологию или язык для создания десктопного приложения. Electron обычно использует уже созданные бизнес-логику, дизайн и общую структуру веб-приложения. Это отличный способ сэкономить время и деньги, как для заказчика, так и для разработчика.
Если вы – JavaScript-разработчик, вам нужно будет изучить совсем немного относительно простых вещей о том, как работает Electron и его API. Скорее всего вы сумеете создать свое первое десктопное приложение всего за несколько дней.
Electron это состоявшаяся технология с растущим сообществом, благодаря чему может считаться отличным рабочим окружением. Благодаря движку Chromium для UI-рендеринга вы получаете доступ к таким инструментам как Developer Tools и Storage Access.
#2 NW.js
Следующим в нашем списке идет NW.js, прежде известный как node-webkit. Он был создан в Центре технологий с открытым исходным кодом компании Intel путем комбинирования фреймворка Node.js и движка Chromium (прежде известного как Webkit).
Благодаря комбинации Node.js и Chromium вы можете создать приложение, которое будет не только загружать локальный вебсайт в окне приложения, но также коннектиться к OS через JavaScript API. Подобное решение позволяет вам контролировать такие параметры как размеры окна, панель инструментов и пункты меню, а также обеспечивает доступ к локальным файлам на компьютере.
NW.js не догматичен; он предоставляет вам свободу выбора фреймворков и библиотек, которые вы хотели бы использовать в своем проекте. Он дает возможность вызывать модули Node.js прямо из DOM, поддерживает все особенности браузера, обеспечивает защиту исходного кода JavaScript. Доступен для Linux, Mac OS и Windows.
#3 AppJS
AppJS это простой, но мощный инструмент. С его помощью можно создавать кросс-платформенные приложения, и для этого вам не придется изучать новые языки. Подобно уже упомянутым библиотекам, для работы с AppJS вам понадобятся только знания HTML, CSS и JavaScript.
Если сравнивать AppJS, Electron и NW.js, то AppJS это старейший Node.js-Chromium фреймворк. Но он даже отдаленно не такой зрелый, как его собратья. И поскольку он «выдохся», то может быть не лучшим выбором для новых проектов.
С AppJS вы получаете:
- JS, HTML5, CSS, SVG, WebGL от Chromium
- зрелые http/https-сервера и клиентские API – Node
- файловую систему, DNS, шифрование, подпроцессы, OS APIs – Node
- виртуальные машины со средой для изолированного выполнения кода – Node
- инструменты для отображения встроенных привязок C++ к JavaScript – Node
#4 Meteor
Meteor рекламирует себя как «быстрейший способ создания JavaScript-приложений» и «платформу с открытым кодом для мобильной, десктопной и веб-разработки». Этот кросс-платформенный фреймворк написан на Node.js.
Хотя у вас и не получится создавать десктопные приложения с помощью одного лишь Meteor, вы можете использовать его совместно с Cordova или другими похожими инструментами.
Meteor использует MongoDB, протокол распределенных данных (Distributed Data Protocol) и шаблон публикации-подписки для автоматической рассылки изменений без вмешательства разработчика. В нем есть фронтенд- и бэкенд-модули, включая API, инструменты, пакеты Node.js.
#5 Proton Native
Proton Native выпущен недавно. Он стал доступен на GitHub в начале 2018. Proton Native для разработки десктопных приложений делает примерно то же, что React Native сделал для мобильной. Узнать больше о разнице между React.js, React Native и React VR можно здесь.
Это один из лучших JavaScript-фреймворков для десктопных приложений, поскольку позволяет вам управлять состоянием и без заминок строить кросс-платформенные пользовательские интерфейсы. Его работа отличается от работы Electron, который запускает целый браузер Chromium для управления маленьким GUI. Proton Native, в свою очередь, использует собственные инструменты, занимает меньше места и потребляет меньше ресурсов.
У этого решения есть и другие преимущества: Proton Native использует тот же синтаксис, что и React Native, работает с библиотеками React (включая Redux) и совместим с Node.js.
Итоги
В целом, JavaScript-фреймворки для десктопных приложений можно разделить на три категории:
- Фреймворки для создания десктопных приложений на базе веб-браузеров – в их основе лежат Node.js и Chromium (Electron, NW.js, AppJS).
- Фреймворки, нуждающиеся в дополнительных инструментах типа Cordova (Meteor).
- Фреймворки, использующие исключительно собственные инструменты для создания десктопных приложений (Proton Native).
Выбор зависит только от вас и в основном зависит от типа проекта, над которым вы работаете.
Надеемся, что этот краткий список поможет вам обратить внимание на данные инструменты и облегчит процесс принятия решения.
Создание программ под ПК стало возможным, после появления библиотек подобных Electron JS. В ходе урока вы создадите полноценную ПК программу используя JS, HTML и CSS.
Приложения в вебе становятся всё мощнее, здесь прогресс на лицо. Тем не менее значительную долю разработки занимают стандартные приложения, которые имеют полный доступ к физическому оборудованию ПК. Уже сегодня есть возможность объединить обе технологии и написать десктопное приложение на известных языках веб-программирования, вроде HTML, JS и Node.js. Это всё можно поместить в исполняемый файл, который можно использовать на Mac OS X, Windows, Linux.
Сейчас есть 2 популярнейших проекта с opensource-кодом, которые могут создавать исполняемые файлы из веб-приложений. Речь идёт о NW JS и Electron. В рамках данного материала поговорим о последнем.
Начало работы с Electron
Приложения, созданные посредством Electron – это обычные веб-сайты, которые запускаются посредством предустановленного веб-обозревателя Chromium. В добавок к классическим стандартам API HTML5, есть возможность применять весь список модулей Node.js и уникальных функций Electron. Модули сервиса как раз и обеспечивают доступ к ОС.
Запуск приложения
Как уже удалось определить, приложение на Electron – это обычная Node.js программа, поэтому ей нужно добавить npm. Благо, это выполняется предельно легко.
Следует запустить терминал и находясь в каталоге целевого проекта выполнить команду:
npm install
В результате появится папка с названием node_modules
, в которой установлены все нужные зависимости для программы. Дальше стоит ввести ещё одну команду.
npm start
После неё приложение запустится в новом окне. Нужно заметить, что в нём будет исключительно верхнее меню.
Несложно заметить, что приложение включается крайне неудобно для рядового пользователя. Это лишь один из способов запуска, который скорее подходит для разработчика, чем пользователя. После упаковки программы, пользователь сможет включить приложение стандартным способом – дважды кликнуть по ярлыку.
Разработка программы
Разработка программы это создание главного JS файла, а также HTML и CSS файлов содержащих все стили и разметку для страниц программы.
Предлагаем вам просмотреть небольшое видео, в котором наглядно приведено описание и создание полноценного приложение на Electron JS.
Ссылки из видео:
- Установить Node JS;
- Официальный сайт Electron JS;
- Скачать редактор Atom;
- Онлайн программа «Front-end разработчик».
Ниже приведен весь код из видео урока.
JS файл:
const path = require('path');
const url = require('url');
const {app, BrowserWindow} = require('electron');
let win;
function createWindow() {
win = new BrowserWindow({
width: 700,
height: 500,
icon: __dirname + "/img/icon.png"
});
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}));
win.webContents.openDevTools();
win.on('closed', () => {
win = null;
});
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
app.quit();
});
HTML код:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>itProger App</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>
<h1>Конвертер температуры</h1>
<div class="form-group col-md-3">
<label for="usr">Цельсии:</label>
<input type="text" class="form-control" id="celcius" onkeyup="celciusToFahrenheit()">
</div>
<div class="form-group col-md-3">
<label for="pwd">Фаренгейты:</label>
<input type="text" class="form-control" id="fahrenheit" onkeyup="fahrenheitToCelcius()">
</div>
<script>
require('./render.js');
function celciusToFahrenheit(){
let celcius = document.getElementById('celcius').value;
let fahrenheit = (celcius* 9/5) + 32;
document.getElementById('fahrenheit').value = fahrenheit;
}
function fahrenheitToCelcius(){
let fahrenheit = document.getElementById('fahrenheit').value;
let celcius = (fahrenheit - 32) * 5/9
document.getElementById('celcius').value = celcius;
}
</script>
</body>
</html>
Упаковка и дистрибуция
Существует ещё один важный момент, который помогает достичь целевого пользователя. Вам нужно запереть всё содержимое в исполняемый файл, как раз его и можно включить двойным кликом. Важно создать уникальный дистрибутив под каждую ОС: Windows, OS X, Linux. Как раз в этом и пригодится Electron Packager.
Здесь следует уделить внимание тому, что в готовый файл также добавятся ваши ресурсы, это обусловлено платформой Node JS, и обрезанная копия webkit
веб-обозревателя. На выходе должен получиться файл весом около 50 Мб. Это весьма большой вес для обычного приложения в несколько строк. Вопрос с весом программы теряет актуальность при разработке крупных приложений со сложными алгоритмами работы.
Заключение
Главное из значимых отличий от NW JS сводится к тому, что в NW.js входной файл – HTML, в то время как в Electron – JavaScript-файл. Таким образом Electron дарит больше возможностей по контролю. На его основе можно создать приложение с несколькими окнами, и настроить перенос данных между ними.
При помощи Node.js мы можем легко создавать веб-приложения. Теперь благодаря библиотеке node-webkit мы также можем создавать с помощью Node.js десктопные приложения, используя уникальную комбинацию HTML5 и Node.
Введение
В этой библиотеке движок WebKit и Node.js сочетаются уникальным образом. WebKit и Node используют один и тот же контекст (* среда исполнения программы. Здесь и далее прим. пер.), благодаря чему вы можете писать код таким образом, как если бы его предполагалось исполнять в браузере, но с возможностью использовать весь функционал Node.
Список вариантов использования неограничен. Вы можете создавать бизнес-приложения, текстовые и графические редакторы, игры, презентации, панели управления для администраторов и т. д. Просто назовите имя десктопного приложения, которое вы бы хотели создать, и я вам гарантирую, что его можно будет создать при помощи node-webkit.
В данном руководстве я покажу вам, как начать использовать библиотеку на примере создания простого текстового редактора.
Подготовка
Для начала вам необходимо получить библиотеку. Скачайте подходящую для вашей операционной системы версию (само же приложение будет запускаться на всех платформах) с github и распакуйте ее, где захотите. Теперь давайте создадим базовую структуру папок. У нас будут папки для файлов HTML (.html
) и для файлов JavaScript (.js
). Также создайте файл package.json
в той же папке, в которой расположен исполняемый файл nw
, и папку node_modules
для размещения модулей, которые мы создадим.
Package.json
Первым делом необходимо заполнить необходимые поля в фале package.json.
В случае использования node-webkit это name
и main
(на Github вы можете ознакомиться со всем списком доступных опций для package.json
). Первое поле то же самое, что и для простых приложений Node.js. Во втором поле должен быть указан путь (относительный или абсолютный) к
главному файлу HTML, который будет показан при запуске приложения. В нашем случае файл package.json
должен выглядеть следующим образом:
{ "name": "simple-text-editor", "main": "./html/index.html" }
Теперь при запуске приложения с помощью исполняемого файла nw
вы должны будете увидеть пустой экран вроде этого:
Главный файл
Создание пользовательского интерфейса при помощи node-webkit не отличается от создания веб-страницы (кроме как того, что вам известен движок рендеринга (визуализации), в связи с чем вам не нужно предоставлять каких-либо альтернатив для более старых браузеров или использовать библиотеки (jQuery и т. д.) – и, собственно, не стоит, поскольку они содержат код запасных вариантов, что может замедлить ваше приложение). Давайте создадим файл index.html
, который будем использовать:
<!DOCTYPE html> <html> <head> <title>Simple Text Editor</title> </head> <body> </body> </html>
Давайте также подключим (и создадим) главный файл JavaScript, который будем использовать, в теле документа так, что он будет выполняться после загрузки DOM (* Document Object Model – объектная модель документа).
<script src="../js/main.js"></script>
Сейчас при открытии приложения единственным, что изменилось, будет заголовок.
Давайте воспользуемся некоторыми возможностями Node
Для демонстрации простоты использования возможностей Node.js при помощи node-webkit, давайте прочтем содержимое package.json
и выведем его на экран. Создайте js/main.js
и напишите в нем следующий код:
var fs = require('fs'); fs.readFile('./package.json', 'utf-8', function (error, contents) { document.write(contents); });
Как вы видите, код выглядит так же, как если бы вы писали его для Node. Но затем мы используем document.write
для выведения содержимого файла на страницу. Нет необходимости в установке какого-либо локального сервера.
Теперь откройте приложение и вы должны будете увидеть нечто подобное:
Модули
У node-webkit есть еще одно преимущество: нет необходимости в написании тегов <script>
в вашем HTML, если вы хотите разбить ваш код на модули. Вы можете это сделать так же, как и в Node.js – при помощи require
(* функция Node для импорта модулей). Давайте создадим простой модуль для выведения содержимого файла в текстовое поле или для записи его в файл. Создайте файл file.js
и поместите его в папку node_modules.
Теперь напишите в нем следующий код:
var fs = require('fs'); function File() { function open(path, document) { } function save(path, document) { } this.open = open; this.save = save; } module.exports = new File;
Как вы видите, это будет один класс со статическими свойствами (* принадлежат непосредственно классу, то есть доступные из него «через точку») – двумя публичными методами: один – для открытия файлов, а другой – для их сохранения.
Метод open
будет выглядеть следующим образом:
function open(path, document) { fs.readFile(path, 'utf-8', function (error, contents) { document.getElementById('editor').value = contents; }); }
Довольно просто, да? Функция принимает в качестве первого параметра путь к файлу и помещает содержимое файла в элемент с id= «editor». Нам также необходимо передать объект document
в функцию, поскольку скрипт вызывается при помощи функции Node require
и не имеет доступа к объектам WebKit напрямую.
Метод save
так же прост, как и предыдущий:
function save(path, document) { var text = document.getElementById('editor').value; fs.writeFile(path, text); }
Теперь давайте проверим, все ли работает. Замените содержимое js/main.js
на следующее:
var file = require('file.js'); console.log(file.open, file.save);
Если вы теперь перейдете в консоль инструментов разработчика и кликнете кнопку developer refresh в правом углу окна, то должны будете увидеть определения двух функций из этого модуля. Это еще одно преимущество node-webkit – вызовы console.log
отображаются в консоли инструментов разработчика, благодаря чему легче проводить отладку вашего приложения.
Реализация полей для отправки файлов в Node-Webkit
Давайте добавим два поля для отправки файлов, которые нам понадобятся позже:
<input id="open" type="file" style="display:none" accept="text/*"/> <input id="save" type="file" nwsaveas style="display:none" accept="text/*"/>
Обратите внимание на атрибут nwsaveas
во втором поле. Это – специальный тип поля для ввода в node-webkit, который позволяет пользователю выбрать несуществующий файл. Оба поля скрыты, поскольку нам необходим к ним доступ только из JavaScript. В node-webkit поля для отправки файлов модифицированы таким образом, что вы можете сгенерировать событие click
на них, благодаря чему есть возможность открыть диалоговое окно для проведения операций с файлом без участия пользователя, кликающего по полю (без необходимости применения обходных приемов, например, скрытых полей выше кнопки). Теперь мы можем перейти к JavaScript
Для начала удалите вызов console.log
из файла js/main.js.
Теперь напишите в нем следующий код:
function clickInput(id) { var event = document.createEvent('MouseEvents'); event.initMouseEvent('click'); document.getElementById(id).dispatchEvent(event); } document.addEventListener('keyup', function (e) { if (e.keyCode == 'O'.charCodeAt(0) && e.ctrlKey) { clickInput('open'); } else if (e.keyCode == 'S'.charCodeAt(0) && e.ctrlKey) { clickInput('save'); } });
Благодаря этому коду реализуется возможность показа диалоговых окон для открытия (Open) и сохранения (Save) файла. Главную работу выполняет функция clickInput
– она симулирует событие click по элементу для ввода; в обычном браузере это было бы невозможно по соображениям безопасности, однако здесь это вообще не является угрозой безопасности. Далее идет обычный обработчик событий для keyup
, в котором проверяется, была ли нажата верная комбинация клавиш (Ctrl+O или Ctrl+S), и осуществляется «клик» по полям для ввода. Обратите внимание, что вышеописанный функционал невозможно реализовать в браузере – комбинации клавиш такие как Ctrl+O и Ctrl+S зарезервированы браузером для внутреннего использования и никаких событий не генерируется при их нажатии (за исключением Firefox).
Теперь нажмите кнопку developer refresh, и вы должны будете увидеть соответствующее диалоговое окно при нажатии Ctrl+S или Ctrl+O. Они, конечно же, пока ничего не выполняют.
Создание редактора
Теперь, поскольку мы будем создавать текстовый редактор, нам необходимо создать что-то, на чем можно писать. Добавьте textarea
в HTML:
<textarea id="editor" style="position:fixed;top:0;bottom:0;left:0;right:0"></textarea>
Далее нам нужно завершить код для открытия/сохранения файла. Давайте создадим слушатель событий события onchange
для полей open
и save
:
document.getElementById('open').addEventListener('change', function (e) { file.open(this.value, document); }); document.getElementById('save').addEventListener('change', function (e) { file.save(this.value, document); });
Благодаря ранее созданному модулю код очень прост. Создание редактора возможно благодаря еще одной особенности node-webkit: в отличие от браузеров (опять по соображениям безопасности), где значением элемента поля для отправки файлов является ненастоящий путь, у нас значением является выбранный путь. Теперь откройте приложение (или нажмите кнопку developer refresh, если вы его не закрыли), и у вас должен быть отлично работающий текстовый редактор.
Дальнейшее усовершенствование
Мы также можем сделать еще кое-что, чтобы улучшить редактор и сделать его более полезным. Например, давайте добавим возможность открывать новое окно, когда пользователь нажимает Ctrl+N. Для начала добавьте require
в верхнюю часть скрипта:
var gui = require('nw.gui');
Модуль nw.gui
– это библиотека node-webkit, с помощью которой реализуется интерфейс пользователя (вы можете почитать больше о нем на Node-webkit Github). Далее добавьте эту конструкцию else if
к слушателю события keyup
в документе:
} else if (e.keyCode == 'N'.charCodeAt(0) && e.ctrlKey) { gui.Window.open('index.html'); }
И viola! Если вы обновите приложение, то теперь можете нажать Ctrl+N для открытия нового окна. Тем не менее, эта функция действительно отличается от обычной window.open.
Вы можете передавать различные опции для window в качестве второго параметра. Список возможных настроек доступен в документации.
Еще одна штука, которая может быть полезна в текстовом редакторе, – это меню приложения (то, которое расположено под строкой заголовка в Windows/Linux и находится в верхней части экрана в Macintosh). В node-webkit реализовать меню довольно просто. Для начала давайте создадим его:
var menu = new gui.Menu({ type: 'menubar' });
Тип menubar
зарезервирован для меню приложения. Теперь мы можем добавить в него пункты (* которые в свою очередь могут быть подменю). Пускай это будет File
:
menu.append(new gui.MenuItem({ label: 'File', submenu: new gui.Menu() }));
Теперь давайте добавим в него некоторые пункты:
menu.items[0].submenu.append(new gui.MenuItem({ label: 'New', click: function () { gui.Window.open('index.html'); } })); menu.items[0].submenu.append(new gui.MenuItem({ type: 'separator' })); menu.items[0].submenu.append(new gui.MenuItem({ label: 'Close', click: function () { gui.Window.get().close(); } }));
menu.items[0]
– это первый пункт меню нашего приложения (вы также можете при желании присвоить его значение переменной при его создании). Мы добавляем новые пункты к его submenu
(* подменю) и для каждого определяем функцию обраного вызова для обработки события click
при клике по пункту. Метод gui.Window.get
принимает текущее окно так, что мы можем его закрыть, когда пользователь выбирает опцию Close в File.
Наконец, мы можем присвоить меню окну:
gui.Window.get().menu = menu;
Опять-таки, мы используем gui.Window.get
для получения текущего окна; затем мы присваиваем наше меню в свойство menu
окна. Пожалуйста, обратите внимание, что хотя мы и можем присваивать каждому окну различные меню, в OSX (Macintosh) одно приложение может иметь только одно меню (общее для всех окон), так что если вы хотите, чтобы ваше приложение использовалось на Macintosh, вам необходимо избегать использования разных меню для различных окон.
Если вы теперь откроете или обновите ваше приложение, то вы должны будете увидеть системное (* главное) меню под строкой заголовка.
Упаковка приложения
Если теперь вы захотите поделиться вашим приложнеием в другими пользователями, то вы можете упаковать его в один файл так, что пользователи смогут скачивать исполняемый файл node-webkit, соответствующий их платформе, и запускать ваше приложение с его помощью. Для начала давайте удалим панель инструментов, из-за которой окно выглядит подобно браузеру, – оно может пригодиться при разработке, однако ваши пользователи, скорее всего, не захотят его видеть. Мы можем это осуществить, задав значением window.toolbar
false
в package.json
, и теперь файл выглядит так:
{ "name": "example-app", "main": "./html/index.html", "window": { "toolbar": false } }
Если вы теперь откроете приложение (ничего не поменяется, если вы просто обновите его, поскольку package.json
загружается только при запуске), то должны будете увидеть конечный результат:
Упаковать приложение очень просто. Нужно только создать zip-архив
со всеми своими ресурсами (всеми созданными вами файлами и без файлов, поставляемых с node-webkit) и изменить его разрешение на .nw.
Вот и все. Если пользователь скачает node-webkit и ваш модуль, то нужно будет всего лишь поместить его в папку node-webkit и запустить исполняемый файл nw.
Детальная информация с дополнительными рекомендациями доступна на node-webkit GitHub.
Теперь ваш редактор можно предоставить пользователям.
Заключение
Как вы видите, node-webkit – это очень многообещающая и мощная библиотека. Поскольку часто происходит обновление и исправление багов, то маловероятно, что развитие этого проекта будет прекращено, что иногда случается с проектами с открытым исходным кодом.
Поделитесь своими мыслями о проекте ниже в комментариях. Лично я считаю, что это лучшее решение для создания десктопных приложений при помощи Node.js и HTML.
Кстати, вы заметили как Chromium похоже на Canonium? — Раньше я даже и не думал об этом.
Если вы читаете любую статью из этой серии, то подразумевается, что вы хоть как-то знакомы с Node.js и в состоянии прочесть документацию по нему.
У меня уже руки чешутся начать! А у вас?
План
Как по мне, то материал проще усваивать небольшими порциями. Поэтому мне нужно указать некое содержание:
- Введение
- Самое необходимое
- Подготовка
- Архитектура приложения
- Первый билд приложения
- Альтернативный способ сборки
- Интерфейс NodePad
- Выводы
- Логика приложения
- Подготовка к релизу
Самое необходимое
В этот раз обойдёмся без теории, которую так упорно прокручивает большая часть заходящих на страницы моего блога. Вместо этого будет краткое описание технологий, которые мы будем использовать.
Node.js
Вместо того, чтобы перепечатывать доступное для каждого определение Node.js, я просто оставлю его тут:
Node или Node.js — программная платформа, основанная на движке V8 (транслирующем JavaScript в машинный код), превращающая JavaScript из узкоспециализированного языка в язык общего назначения. Node.js добавляет возможность JavaScript взаимодействовать с устройствами ввода-вывода через свой API (написанный на C++), подключать другие внешние библиотеки, написанные на разных языках, обеспечивая вызовы к ним из JavaScript-кода.
Chromium
Chromium — это веб-браузер с открытым исходным кодом, на котором строится всем известный Google Chrome. Каждый день браузер обновляется, и в нём происходят какие-то перемены: от исправления багов до внедрения нового функционала. Собственно говоря, большинство (если не все) фишек из Chromium рано или поздно переползают в Chrome.
node-webkit
node-webkit — это среда выполнения приложения, которая сочетает в себе Chromium и Node.js, позволяющая создавать кроссплатформенные приложения с интерфейсом. Иначе говоря, node-webkit позволяет создавать такие же веб-приложения, которые уже сейчас есть в браузере (StackEdit, например) в некий оффлайн. Разумеется, если функционал веб-приложения это позволяет, и в этом есть хоть какой-то практический смысл. При этом разрабатывается приложение на горячо нами любимом Javascript с применением API node.js, который даёт доступ к системе пользователя, позволяя уйти за рамки обычных возможностей браузера.
Grunt.js
Grunt — это инструмент для сборки проектов из командной строки с использованием задач. В рамках этой статьи он нужен для построения нашего приложения под три популярные операционные системы.
Подготовка
Перед тем как начать что-то писать, нужно позаботиться об удобстве. Для этого нам и понадобится Grunt.
Внимание
Если по религиозным или иным соображениям вы не можете пользоваться Grunt, то посмотрите в сторону Gulp или главу «Альтернативный способ сборки» в конце этой статьи.
Благодаря Grunt или Gulp (суть не в этом) мы можем автоматизировать сборку нашего приложения, которая будет выполняться тогда, когда мы изменили какой-либо его ресурс.
Просто создаём файл package.json
и пишем в него:
{
"name": "grunt-node-webkit-app",
"devDependencies": {
"grunt": "latest",
"grunt-contrib-watch": "^0.6.1",
"grunt-node-webkit-builder": "latest"
}
}
И файл Gruntfile.js
:
module.exports = function(grunt) {
grunt.initConfig({
watch: {
node: {
files: ['app/**/*'],
tasks: ['nodewebkit'],
options: {
spawn: false,
}
}
},
nodewebkit: {
options: {
platforms: ['win', 'osx', 'linux32', 'linux64'], // Платформы, под которые будет строиться наше приложение
buildDir: './build', // Путь, по которому будет располагаться построенное приложение
},
src: './app/**/*' // Путь, по которому располагаются исходные коды приложения
},
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-node-webkit-builder');
grunt.registerTask('default', ['watch']);
};
Если это ваша первая встреча с Grunt, то вам понадобится Grunt CLI
, который можно установить, написав в консоли:
npm i -g grunt-cli
После этого переходите в папку с проектом и выполняете команду:
npm i
Запускать сейчас ничего не нужно. На этом подготовительный этап завершён.
Если вы хотите подробнее узнать о Grunt, то обратите свой взор на видео от Sorax.
Обновление (17.01.2015)
Существует способ запускать приложения из командной строки, при этом обновлять их содержимое, при необходимости, используя тулбар.
Установим глобально nodewebkit:
npm i -g nodewebkit
Перейдём в директорию с приложением и запустим его (Не забываем точку):
nodewebkit .
После изменения ресурсов приложения воспользуйтесь кнопкой обновления (самая правая в тулбаре). Только не забудьте включить тулбар в package.json
.
Архитектура приложения
Отныне и до конца, нашим местом дислокации будет папка app
. Поэтому переходим в неё и приступаем к проектированию архитектуры.
Для начала всё просто:
project/
└──app/
├── fonts/
│
├── images/
│
├── scripts/
│ └── vendor/
│
├── styles/
│ └── vendor/
│
└── views/
└── app.html
Манифест приложения
Первым делом нам снова придётся создать файл app/package.json
, в котором будет описано наше приложение. Однако, на этот раз не всё так просто. Нужно разбираться.
Обязательные поля
Каждый манифест (package.json) приложения обязан предоставить следующие поля:
- name (string) — Уникальное имя приложения, предоставленное в виде
name-of-application
. Может включать в себя следующие символы:._-
. - main (string) — Страница, которая будет открыта при запуске приложения.
Дополнительные поля
Я не буду перечислять все дополнительные поля, так как количество их довольно велико, поэтому пройдёмся по самым базовым:
- version — Версия приложения по методике семантического версионирования.
- chromium-args (string) — Включение или отключение возможностей браузера Chromium, используя аргументы командной строки. Список аргументов командной строки доступен по этому адресу.
- window — Самое полезное для нас поле, которое содержит подполя:
- title (string) — Заголовок главного окна.
- width/height (int) — Начальная ширина/высота главного окна.
- toolbar (boolean) — Отображение панели навигации.
- icon (string) — Путь до иконки (значка) приложения.
- position (string) — Позиция окна при запуске приложения:
null
илиcenter
илиmouse
. - min_width/min_height (int) — Минимальная ширина/высота окна.
- max_width/max_height (int) — Максимальная ширина/высота окна.
- resizable (boolean) — Разрешение изменения размера окна.
- always-on-top (boolean) — Отображать окно приложения поверх остальных окон.
- fullscreen (boolean) — Разрешение полноэкранного режима.
- show_in_taskbar (boolean) — Отображение на панели задач или в доке. Значение по умолчанию —
true
. - frame (boolean) — Отображение рамок приложения. Установив значение
false
, вы получаете пустое окно с белым фоном. - show (boolean) — Отображение окна приложения при запуске.
Исходя из требований и дизайна будущего приложения, можно достаточно гибко сконфигурировать его на начальном этапе.
Полный список полей манифеста можно посмотреть на этой странице. К слову, на Wiki страницах самого проекта node-webkit, находится много полезной информации.
В нашем случае, package.json
будет содержать в себе следующие поля:
{
"name": "node-webkit-nodepad",
"main": "views/app.html",
"version": "0.1.0",
"window": {
"title": "NodePad",
"toolbar": false,
"frame": false,
"width": 800,
"height": 500,
"min_width": 400,
"min_height": 200,
"position": "center"
}
}
Ах, да, совсем забыл сказать, что мы будем делать простейший блокнот с автоматическим сохранением в Local Storage и возможностью ввода и вывода текста в формате markdown.
Зависимости
Дальнейшие махинации будут проходить с npm (пакетный менеджер node.js). Если вы не знакомы с этим понятием, то вам следует срочно ознакомиться с ним, перейдя по этой ссылке и посмотреть видео, автором которого является Илья Кантор.
Итак, у нас есть идея создания простейшего блокнота с применением техники markdown. Будем сами писать парсер синтаксиса? — Разумеется, нет. Это долго и не нужно, так как велосипед нам уже изобрели и оформили в виде пакета npm.
Переходим на сайт пакетного менеджера npm и отправляемся в поиски необходимого нам пакета.
Открываем консоль и в папке app/
прописываем команду:
npm i markdown --save
Произойдёт загрузка пакета и, благодаря флагу --save
, добавление зависимости к нашему приложению в манифесте:
{
...
"dependencies": {
"markdown": "^0.5.0"
}
}
По идее, нам этого хватит.
Первый билд приложения
Вот и подошли мы к тому, что создаём файл app/views/app.html
:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>My first node-webkit application</title>
</head>
<body>
Content
</body>
</html>
Очень странное приложение, не правда ли? Я тоже вначале ожидал кучу кода и непонятные мне конструкции, но всё оказалось банальным.
А давайте соберём наш первый билд?
Переходим в консоли к папке project
и запускаем grunt командой:
grunt nodewebkit
Начнётся загрузка пакетов для выбранных вами операционных систем:
Внимание
Пакеты загружаются один раз, поэтому не стоит паниковать заранее. Однако, если это не финальная (релизная) сборка приложения, то советую указывать ту операционную систему, под которую вы будете разрабатывать приложение, так как скорость отдачи пакетов может огорчить любого.
Обращу ваше внимание ещё раз на то, что сборку приложения можно автоматизировать, прописав в консоли grunt
и изменив какой-либо его ресурс.
После загрузки пакетов самого node-webkit
сразу же стартует сборка нашего проекта:
Ура! Скорее к приложению!
Переходим в папку project/build/node-webkit-nodepad/
, выбираем свою платформу и запускаем приложение. Работает!
Альтернативный способ сборки
Хотя мне и не хочется говорить про этот способ сборки из соображений его… кхе… нецелесообразности и трудоёмкости, я всё же посчитал должным написать про него.
Альтернативным вариантом будет создание билда приложения вручную. Если в описанном ранее способе сборка производилась автоматически для всех платформ, то в этот раз сборка проводится под каждую платформу конкретно, в тесном сотрудничестве с самой системой. Иначе говоря, если вы хотите собрать своё приложение для всех трёх платформ, то вам понадобится иметь при себе все три операционные системы.
Что-то я устал от Windows, поэтому на этот раз будем использовать OS X Yosemite.
Внимание!
Не стоит паниковать! Команды для сборки будут приведены ниже для Windows и Linux.
Итак, будем считать, что у нас есть директория, в которой находится node-webkit.app
и наше приложение.
Шаг 1
Необходимо создать nw пакет. По сути, это обычный zip архив. Поэтому запускаем терминал, переходим в папку с приложением и пишем команду:
zip -r ../${PWD##*/}.nw *
В ответ система создаст nw файл:
На Linux всё аналогично. А вот для тех, кто использует Windows у меня плохие новости: архив придётся создавать самому и после сменить .zip
на .nw
.
Шаг 2
На втором шаге нужно переименовать получившийся файл в app.nw
, но так как у нас он уже имеет такое имя, то идём дальше.
Шаг 3
Переходим к файлу node-webkit.app
и просим OS X показать нам содержимое пакета.
Переходим в директорию Contents/Resources/
и переносим туда пакет нашего приложения (app.nw). Получится примерно так:
В Linux слегка иначе:
cat /usr/bin/nw app.nw > app && chmod +x app
В Windows тоже всё просто:
copy /b nw.exe+app.nw app.exe
Мы вышли на финишную прямую!
Внимание!
Для работы приложения в Windows и Linux необходимо скопировать следующие файлы из папки node-webkit в папку собранного приложения:
nw.pak
иicudtl.dat
.
Шаг 4
Запускаем наше приложение и радуемся появлению пустого окна
Вывод
Я очень рад, что рассказал про этот способ, так как он даёт базовое понятие того, как собирается ваше приложение. И всё же, будет лучше, если вы не будете использовать этот вариант сборки. Просто забудьте об его существовании.
Если захотите подробнее узнать про описанный здесь метод, то посетите посвящённую этому страницу Wiki. Там же можно ознакомиться с приложениями, которые упрощают сборку до нажатия на одну кнопку «Build».
Интерфейс NodePad
Совсем кратко поговорим о вёрстке нашего приложения, так как верстать, я думаю, умеет каждый.
Интерфейс нашего приложения будет похож на StackEdit, то есть у нас будет одно текстовое поле для ввода данных (разметки Markdown) и справа от него область, в которой мы будем сразу видеть представление этих данных и их изменение в режиме реального времени.
Если вы помните, то node-webkit включает в себя браузер Chromium, а это означает, что:
- Можно использовать все новейшие технологии без опасения, что в каком-нибудь браузере что-то пойдёт не так.
- Не нужно заботиться о кроссбраузерности.
- Вам не нужны браузерные префиксы, если только Chromium не требует их для конкретного свойства.
- Можно не заботиться о количестве запросов на ресурсы, так как выход в интернет нашему приложению не требуется.
Замечательно! Пришло время использовать Flexbox и SVG!
И снова не могу промолчать и утаить от вас ужасную, для многих, истину — всё таки оптимизировать ресурсы придётся. Нет, это не из-за того, что скорость работы node-webkit плохая или ещё чего-то в этом духе. Причина довольно банальная — конечный размер приложения.
Вы уже видели сколько места занимает наше пустое приложение на диске? — В Windows 43 Мб, а это не хухры-мухры. И это без учёта необходимых для его работы библиотек (icudtl.dat
— 10.5 Мб и nw.pak
— 5.7Мб). А теперь прибавьте к этой цифре вес JavaScript библиотек, шрифтов, изображений и остальных ресурсов приложения. Мы уже к сотне подходим. Конечно, если вы будете делать установщик, то размер уменьшится, но до тех пор, пока пользователь не установит приложение. Мне бы не хотелось, чтобы простой блокнот весил под 100 Мб.
Подробнее про размер приложения, его оптимизацию и сжатие мы поговорим в третьей части нашей небольшой серии статей.
Замечание
Я не буду расписывать то, как верстается интерфейс приложения. Просто учтите, что это обычная вёрстка — как и в браузере. Единственным отличием является полная уверенность в том, что написанный у вас код, будет в точности отображён и у пользователей вашего приложения, так как браузер встроен в node-webkit и ничего отключить там просто так нельзя.
Вы уже успели расстроиться? — Да, без вёрстки скучновато жить. Однако, не забывайте, что цель статьи не показать как работает Flexbox и как сверстать пару кнопок, а ознакомиться с node-webkit на реальном примере.
Вывод к первой части
В этой статье мы познакомились с удивительным внешним видом мира node-webkit. Разобрались, как собирать наше приложение, запускать его и проектировать архитектуру на начальном этапе.
В следующей части статьи мы подробнее поговорим про внутреннее устройство нашего приложения и организации его логики. На повестке дня будет:
- Взаимодействие пользователя с интерфейсом.
- Работа с файлами.
- Работа с данными.
- Прочие мелочи.
Постарайтесь не бояться использовать новые инструменты, выглядящие очень сложными снаружи и внутри, но простыми в использовании — это может сэкономить уйму времени и дать новые возможности для вашего творчества в мире веб-разработки.
И напоследок, для желающих, хочу дать пару ссылок в качестве домашнего задания. Перейди по этим ссылкам вы сможете ознакомиться с тем инструментарием, который будет использоваться во второй части.
Домашнее задание
- Документация node-webkit
- Использование Node.js модулей
- Обзор API и уведомлений
- Руководство по использованию нативного UI API
- Файловые диалоги
- Примеры приложений
- Документация Node.js
- Node.JS от А до Я
- Чтение и запись файлов в NodeJS
- Диалоговые и модальные окна
- HTML5 API веб-хранилища
Иии…
Не стоит переживать, это не неожиданный финал статьи, а всего лишь её первая часть. Вторую и третью часть вы сможете найти на страницах блога уже в ближайшее время. Тем временем постарайтесь сохранить в себе желание попробовать в действии node-webkit.