Статьи

Создание веб-приложений для настольных компьютеров с помощью GitHub’s Electron

В этом году в Build Microsoft объявила о выпуске Visual Studio Code , первого редактора кода от Microsoft, который работает на Mac. Полнофункциональный десктоп IDE. И толпа сходит с ума.

Я сам скачал и использовал код Visual Studio, и должен признать, что это довольно хорошо. Особенно учитывая, что это релиз v1, о котором мы здесь говорим.

Но это не займет много времени, чтобы кто-то раскрыл маленький секрет Кодекса …

VS-код

Исходное имя файла atom.exe? Что это значит?

Это означает, что Visuals Studio Code не является настольным приложением Mac в традиционном смысле. Это на самом деле гибридное приложение, созданное с использованием веб-технологий.

GitHub Atom

Atom — это редактор кода, выпущенный GitHub в прошлом году. Как и редакторы кода, он похож на любимого разработчика, Sublime Text, с одним невероятным отличием: сам редактор Atom — это HTML, CSS и JavaScript . Затем он оборачивается в собственную оболочку с доступом к устройству. Это заставляет его выглядеть так, как будто это нативное приложение для настольных компьютеров, и для всех намерений и целей это полностью так. Если вы использовали Atom, вы знаете, насколько он быстр и хорош. Как сказал бы Стив Джобс: «Это не сутулость».

GitHub разделил и открыл исходную часть Atom, которая оборачивает реальный редактор и назвала его Electron (первоначально он назывался Atom Shell).

Это не первый раз, когда мы видели что-то подобное. Редактор скобок Adobe был построен таким же образом. Кроме того, Google сделал это несколько лет назад, когда они выпустили Chrome Packaged Apps . Я работал в команде Telerik, которая создала самое первое приложение для камеры, которое поставляется на ChromeOS. Я прекрасно знаю, что такое создание настольных приложений на основе веб-технологий. Позвольте мне быть полностью честным в моих чувствах к созданию настольных приложений таким образом.

Я ЛЮБЛЮ ЭТО.

Шутки в сторону. Я никогда не создавал настольных приложений так быстро в своей жизни. Я подумал: «Это слишком хорошо, чтобы быть правдой!» И увы, так и было .

Вскоре после того, как были объявлены упакованные приложения, они были отправлены в дальний угол открытой рабочей области Google под мерцающим флуоресцентным светом где-то во влажном подвале здания в Маунтин-Вью рядом с плантатором, полным грибов. Насколько я могу судить, упакованные приложения мертвы.

Я знал, что это было слишком хорошо, чтобы быть правдой. И тогда я встретил Электрон.

электрон

По сути, Electron — это то, чем должны быть упакованные приложения (мое мнение — очевидно). Это блестящая реализация концепции родного приложения-обертки для веб-приложений, которую мы видим настолько популярной в таких технологиях, как PhoneGap. Так что же делает его лучше, чем Chrome Packaged Apps, MacGap или любой другой бесчисленный проект, который уже пытался это сделать?

Одним словом: Стандарты

Electron, в отличие от многих своих предшественников, почти полностью полагается на веб-стандарты, которые вы уже знаете по структуре проекта и модулям. Чтобы показать вам, что я имею в виду, я собираюсь преобразовать сайт, который я сделал для проекта Kendo Flippable, в приложение Electron Desktop. Однако, прежде чем мы сделаем это, я думаю, стоит обсудить, почему нужно создавать настольное приложение.

Когда пойти на рабочий стол

Нам часто трудно определить, когда создавать настольное приложение (нативное) и когда предоставлять веб-приложение. Существует мало того, что один может сделать, а другой — нет. Например, одним из распространенных заблуждений является постановка вопроса следующим образом:

«Требуется ли приложению доступ к устройству?»
A: ДА — собственный рабочий стол
B: НЕТ — Интернет

Тем не менее, рассмотрим, на что способен Интернет:

  • Доступ к камере
  • Микрофон
  • Аудио Манипуляции
  • Файловая система (в определенной степени)
  • Уведомления
  • WebSockets
  • акселерометр

…И список продолжается. Списывание из Интернета, потому что вам нужен доступ к устройству, больше не является аргументом «все или ничего». Итак, каковы законные причины пойти по пути рабочего стола? На мой взгляд, есть два основных преимущества:

  1. Нет проблем совместимости между браузерами
  2. Нет загрузки удаленных активов (без задержки)

Есть и другие потенциальные преимущества, такие как доступ к подключенным устройствам (Bluetooth, USB и т. Д.), А приложения для настольных компьютеров обычно отключены по умолчанию, но это мои личные любимые.

Прелесть настольного приложения в том, что вы объединяете все свои ресурсы и код в пакет, который затем загружается непосредственно с компьютера пользователя. В Интернете вы не представляете, где в конечном итоге будет работать ваше приложение, и вы анализируете каждый байт, проходящий по проводам, чтобы убедиться, что это абсолютно необходимо. Настольные приложения не беспокоятся ни об одном из этих факторов.

В идеале вы должны создать веб-приложение, в котором вы точно знали, в каком браузере будет запускаться ваше приложение (вплоть до версии), и все ваши ресурсы уже были загружены локально.

Это, друг мой, именно то, что Электрон позволяет тебе делать.

Создание нового приложения Electron

Первое, что вам нужно сделать, это получить Электрон. Лучший способ сделать это — установить его через npm.

npm install -g electron-prebuilt

Это установит Electron в вашей системе. Теперь вы можете запускать любое веб-приложение как приложение Electron, при условии, что у вас есть правильная конфигурация в вашем package.jsonфайле, и именно здесь мы возвращаемся к красоте стандартов.

Хорошая вещь о стандартах в том, что у вас есть так много на выбор.

Эндрю С. Таненбаум

А если серьезно, с Электроном интересно работать, потому что он уже использует концепции, с которыми вы, скорее всего, знакомы. Одним из них является package.jsonфайл. Нет глупого манифеста для работы. Все, что вам нужно сделать, чтобы указать, что ваше приложение является приложением Electron, это добавить «основное» поле в package.jsonфайл. Ниже приведен мертвый простой электрон package.json.

{
    "name": "Sample App",
    "version": "0.0.1",
    "main": "main.js"
}

Этот файл `main.js` является ключевым. Его можно называть как угодно, но это файл, который Electron выполнит при загрузке вашего сайта. В этом файле `main.js` должен быть небольшой шаблонный код, чтобы открыть новое окно браузера и запустить шины.

var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the javascript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// This method will be called when Electron has done everything
// initialization and ready for creating browser windows.
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
});

Это было почти дословно взято из документации Electron, но я обрезал несколько ненужных строк (например, отчеты о сбоях и открытие инструментов разработчика при запуске приложения).

Заметьте, как файл требуется в модулях «app» и «BrowserWindow»? Это просто CommonJS. Это вторая блестящая вещь об Электроне. Предполагается, что все ваши файлы JavaScript будут CommonJS. Это означает, что вам не нужно добавлять систему сборки, чтобы получить поддержку модулей для ваших сценариев. Мы реализовали эту же концепцию в NativeScript, и, как мне кажется, так и должно быть.

main.jsФайл будет пытаться загрузить файл с именем index.html. Вот где вы возвращаетесь на чистую веб-территорию. Ваш файл index.html может содержать все, что вы хотите! Любой CSS, который вы хотите, любой JavaScript, который вы хотите. Все, что выходит в Интернет, является законным в Electron.

<!DOCTYPE html>
<html>
<head>
  <title>Kendo Electron</title>
</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>

Если вы уже находитесь в каталоге проекта, теперь вы можете запустить его в Electron, выполнив его electron .из командной строки или терминала.

Привет мир

Приложение против рендера

Прежде чем идти дальше, стоит отметить, как Electron запускает приложения. Существует два потока: 1) поток приложения и 2) поток рендеринга. Поток приложения — это то, что запускает этот main.jsфайл, а поток рендеринга — index.htmlэкземпляр браузера. Причина, по которой это важно знать, заключается в том, что нативные функции электрона доступны только в потоке приложений . Это функция безопасности, которая «помещает в песочницу» поток визуализации, чтобы вредоносный код не мог быть выполнен во время выполнения.

Это немного сбивает с толку, поэтому давайте посмотрим, как это закончится, посмотрев на один из методов Application.

В Windows есть объект API, BrowserWindowкоторый позволяет использовать встроенную функцию индикатора выполнения в Windows. Мы могли бы написать установленный интервал, который запускает этот процесс из main.jsфайла.

// This method will be called when Electron has done everything
// initialization and ready for creating browser windows.
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });

  // if the current platform is windows...
  if (process.platform === 'win32') {
    var counter = 0;
    // every 1 second, increment the progress bar value
    var progress = setInterval(function() {
      if (counter < 1) {
        mainWindow.setProgressBar(counter);
        counter += .1;
      }
      else {
        mainWindow.setProgressBar(0);
        clearInterval(progress);
      }
    }, 1000);
  }

});

Этот API доступен только в Windows. Если вы запустите этот код на Mac, вы получите ошибку. Вот почему все это завернуто в process.platformусловный, убедившись, что платформа win32.

Это все работает в том, main.jsчто является потоком приложения. Что, если мы хотим запустить этот индикатор прогресса из интерфейса? Мы вставили бы кнопку и обработали бы это событие нажатия.

<!DOCTYPE html>
<html>
<head>
  <title>Kendo Electron</title>
</head>
<body>
  <button id="start-progress">Start Progresss</button>

  <script src="index.js"></script>
</body>
</html>
// index.js
var button = document.getElementById('start-progress');
button.onclick = function() {
  // now what?
};

Я разделил JavaScript для пользовательского интерфейса на index.jsфайл и включил его в index.htmlстраницу. Я упоминал, что Electron рассматривает все файлы JavaScript так, как будто они являются модулями CommonJS. Это может заставить вас думать, что вы можете просто экспортировать BrowserWindowобъект из main.jsфайла и включить его в index.jsфайл. Из-за ограничений нативных методов из потока рендеринга это не будет работать. Модуль появится не определено.

Чтобы выполнить методы потока приложения из потока рендеринга, вы должны использовать то, что называется ipcмодулем.

Модуль IPC для потоковой связи

Это действительно простые вещи. Фактически, это именно то, что я делал в то время, когда создавал упакованные приложения Chrome, поскольку у упакованных приложений есть аналогичные ограничения относительно того, какой тип кода разрешено запускать в том же потоке, который имеет доступ к собственным функциям. Я даже с открытым исходным кодом этого проекта в качестве Pkgплагина для JQuery.

ipcМодуль невероятно похож. Вы отправляете сообщение из потока рендеринга, а основной поток его забирает.

Сначала мы запрашиваем ipcмодуль в main.jsфайле, а затем прослушиваем событие, которое мы в конечном итоге отправим из index.jsфайла.

var app = require('app');  // Module to control application life.
var BrowserWindow = require('browser-window');  // Module to create native browser window.
var ipc = require('ipc');

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the javascript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// This method will be called when Electron has done everything
// initialization and ready for creating browser windows.
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });

});

// respond to the 'progress' event
ipc.on('update-progress', function(event, arg) {
  if (process.platform === 'win32') {
    mainWindow.setProgressBar(arg);
  }
});

Нам просто нужно отправить событие `update-progress` от нажатия кнопки. Для этого требуется тот же модуль ipc, а затем просто выполнить `send` с любыми параметрами, которые мы хотим передать. В данном случае это текущее значение счетчика.

// index.js
var ipc = require('ipc');

var button = document.getElementById('start-progress');
button.onclick = function() {
  var counter = 0;

  // increment the progress value by 0.1 every second
  var progress = setInterval(function() {
    if (counter < 1) {
      ipc.send('update-progress', counter);
      counter += 0.1;
    }
    else {
      // reset the progress value to 0;
      ipc.send('update-progress', 0);

      // clear out the set interval
      clearInterval(progress);
    }
  }, 1000);
};

Нажатие на кнопку теперь обновляет индикатор выполнения на значке панели задач. Кроме того, значение для индикатора выполнения передается из пользовательского интерфейса в поток визуализации. И это все, что нужно сделать.

Особенности родного иша

Ранее в статье я упоминал о том, сколько может сделать только Интернет сам по себе. Он может сделать так много вещей, просто родных, просто получив небольшое разрешение от пользователя. Поскольку пользователь будет запускать наше приложение Electron локально, нам не нужно запрашивать разрешение, оно у нас уже есть.

В качестве примера, мы могли бы удалить собственное уведомление, просто используя API веб-уведомлений.

var ipc = require('ipc');

var button = document.getElementById('start-progress');
button.onclick = function() {
  var counter = 0;

  // increment the progress value by 0.1 every second
  var progress = setInterval(function() {
    if (counter < 1) {
      ipc.send('update-progress', counter);
      counter += 0.1;
    }
    else {
      // reset the progress value to 0;
      ipc.send('update-progress', 0);

      // clear out the set interval
      clearInterval(progress);

      // display a notification saying that we did everything
      new Notification('Progress Test Finished!');
    }
  }, 1000);
};

Now we can add in any JavaScript or CSS library we want. In my case, I built a small application that searches iTunes for artists, displays their albums, and plays the track samples. Of course, I used Kendo UI Core to build the application. Is there any other JavaScript library? :)

Here is a quick video of my demo application, including a native desktop notification.

Note: If the video is blurry, make sure you are watching it in 720p.

Introducing Proton

Visual Studio and Github aren’t the only shops in town building IDEs on Electron. Telerik is currently working on a full fledged desktop client (code name “Proton”) for building iOS and Android apps on any operating system. You may know this IDE in it’s current incarnation as the AppBuilder Windows Client. It’s been one of our most popular editors to date for building mobile applications, but it is currently Windows only (built in WPF). Electron gives us the power to take this same functionality, and make a cross-browser mobile development environment that you can love no matter what platform you’re on.

Proton Beta Testers Wanted

We’re getting close to releasing this new desktop IDE, but we need your help. If you’re interested in getting your hands on an early release of Proton and don’t mind giving us your raw feedback, we’d love to hook you up. Contact @rdlauer on Twitter. He’s the keeper of the Proton keys and will make sure you’re one of the first ones to experience this exciting new IDE

Thank You Notes

I wanted to wrap-up by sending out a “Thank You” to GitHub and the folks that work there for all they’ve done on Electron. Open source projects are sometimes a thankless job where mostly all you hear about are the problems your software has, not all of the wonderful things it does right. I’m incredibly thankful that GitHub has ressurected the ashes of what I loved about Google Packaged Apps and improved it many times over.

You can find the code for the iTunes Artist Search app on GitHub. Just make sure you have Electron Prebuilt installed and you’re good to go.

Make sure you also grab Kendo UI Core, either from Bower, JSPM, GitHub or however you like to install your packages. I really enjoyed putting this project together and had a lot of fun working with Kendo UI and Electron. Desktop development should be as easy as web development, and Electron + Kendo UI makes that a reality.