План

Как по мне, то материал проще усваивать небольшими порциями. Поэтому мне нужно указать некое содержание:

К слову, недавно вышел новый релиз node-webkit, который исправляет проблему работы с кодировкой. Всем нужно обновиться и радоваться тому, что приложения теперь нормально работают с кодировкой :)

Исправление ошибок

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

Было:

$('#window-resize').on("click", function() {
  if ($( this ).hasClass('maximize')) {
    ...
    $( this ).children('i').toggleClass('icon-toolbar-maximize icon-toolbar-minimize');
  } else {
    ...
  };
});

Стало:

$('#window-resize').on("click", function() {
  if ($( this ).hasClass('maximize')) {
    ...
  };

  $( this ).children('i').toggleClass('icon-toolbar-maximize icon-toolbar-minimize');
});

Оптимизация jQuery

Если вспомнить предыдущую статью из этой серии, то можно заметить, что у нас в файлах содержится довольно много jQuery-запросов в виде:

$('#editor-input')
$('#editor-preview')

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

Файл windowEvents и editor:

...
$              = global.jQuery,
$editorInput   = $('#editor-input'),
$editorPreview = $('#editor-preview');

Помимо jQuery-запросов, у нас имеется событие клика на кнопку-переключатель полноэкранного режима, где применяется jQuery-метод .children(), который работает куда медленнее, чем его «отец» метод .find() примерно в три раза. Путём логических размышлений и проведения теста, принимаем решение убить подлеца!

$('#window-resize').on("click", function() {
  if ($( this ).hasClass('maximize')) {
    ...
  };

  $( this ).find('i').toggleClass('icon-toolbar-maximize icon-toolbar-minimize');
});

Помимо этого, говорят, что одной из так называемых «best practice» является следующее утверждение:

Когда цепочка вызовов метода слишком длинная и не помещается в одну строку (больше 80 символов), следует писать один вызов метода на строке. При этом соблюдать контекстную иерархию отступов (дополнительный отступ).

К счастью, такое место у нас одно, и с ним мы уже работали выше:

$('#window-resize').on("click", function() {
  if ($( this ).hasClass('maximize')) {
    ...
  };

  $( this )
    .find('i')
      .toggleClass('icon-toolbar-maximize icon-toolbar-minimize');
});

Внимательный, матёрый JavaScript программист заметит, что у меня используются разные кавычки ('string и "string"). В своё оправдание скажу лишь одно — это всего лишь привычка. Мне нравится идеология того, что всюду (почти) нужно использовать один вид кавычек, но куда больше мне нравится следующая модель:

  • Одиночные кавычки всюду, кроме указанных ниже мест.
  • Двойные кавычки для указания событий.

Однако, нужно придерживаться рекомендаций, и поэтому я борюсь с этим недугом. Следствием этой борьбы будет приведение к единому стилю написания кавычек и в этом проекте.

Увы, иногда так бывает, что увлекаешься и как-то не до соблюдения стилей. Но вскоре я всё таки переделаю Raptorius, чтобы он успевал за мной и проверял мой код на соответствие стандартам.

На этом оптимизацию jQuery можно завершить. Конечно, если вы всё ещё не хотите его выбросить из проекта или заменить более «лёгким» аналогом.

Все изменения в коде, на этом этапе, можно посмотреть в этом коммите на GitHub.

Оптимизация ресурсов

В этой части статьи нужно всего лишь оптимизировать ресурсы приложения.

На что следует обратить внимание:

  • Картинки и шрифты
  • Лишние ресурсы

По ясным причинам, удалять из репозитория Less файлы я не буду, а вот если бы наш блокнот отправлялся в продакшен, то сделать это было бы неплохо.

Также можно осуществить минификацию скриптов и стилей, но никаких преимуществ это не даст.

Семантическое версионирование

Не забывайте следовать семантическому версионированию и старайтесь своевременно обновлять версию приложения.

В нашем случае следует увеличить ПАТЧ, так как мы сделали обратно совместимые исправления.

Все изменения в коде, на этом этапе, можно посмотреть в этом коммите на GitHub.

Попутно стоит напомнить о том, что можно встроить в приложение напоминалку о выходе новых версий приложения, например, по средствам пакета github-update-checker, если основной репозиторий у вас находится на GitHub.

Иконки

Сейчас NodePad у нас безликий — это плохо, так как он имеет стандартную иконку поставляемую node-webkit. А вдруг завтра все начнут использовать node-webkit? NodePad потеряется среди остальных приложений! Так нельзя.

Автоматический способ

Проще некуда, нужно:

  • Создать директорию icons вне app/, чтобы лишние файлы не были в программе при сборке.
  • Добавить в директорию icons/ два файла: icon.ico (Windows) и icon.icns (OS X).
  • Слегка изменить Gruntfile.

Превратить PNG в ico и icns можно, используя ICONVERT ICONS. Но, как это известно дизайнерам, вручную куда лучше — можно контролировать каждый пиксел.

Итак, дополнение файла настроек Grunt будет содержать всего две строки:

...
macIcns: './icons/icon.icns',
winIco: './icons/icon.ico'
...

Теперь NodePad обрел лицо в Windows:

Иконка NodePad в Windows

И похорошел в OS X:

Иконка NodePad в OS X

Для добавления иконки в Linux версию приложения пока что автоматического метода нет.

Ручной способ

На этот раз всё скучнее и дольше. При этом необходимо иметь все три платформы под рукой.

Windows

Придётся скачать Resource Hacker и прочитать небольшую заметку, в которой говорится буквально следующее:

  • Открыть exe файл
  • Перейти в директорию icon -> 1 -> ...
  • Выбрать в меню заменить ресурс и найти предварительно созданный ico файл
  • Сохранить exe

OS X

С иконкой для OS X куда интереснее.

  • Превращаем PNG в icns файл.
  • Перенести файл в .app/Contents/Resources и переименовать его в nw.icns.
  • Указать иконку в файле .app/Contents/Info.plist, если вы не хотите переименовывать файл.

Linux

Для Linux нужно курить этот мануал.

Сжатие

Как и говорилось ранее, размер NodePad внушающий:

  • Windows: ~65МБ
  • OS X: ~90МБ (без учета веса иконки)
  • Linux: ~100МБ

Если сказать, что я офигел от цифр, то это не будет передавать моё эмоциональное состояние :)

И на этой грустной ноте приходит на помощь — UPX.

UPX — это упаковщик исполняемых файлов, который поддерживает множество форматов, включая COM, EXE, SYS. После упаковки файлы занимают минимум места на диске, но платой за это будет распаковка приложения непосредственно в память при запуске.

Если вы готовы заплатить снижением скорости запуска приложения, то результаты получаются неплохие (настройки UPX по умолчанию):

NodePad.exe

Исходный размер — ~47МБ. После UPX — ~18МБ.

Согласитесь, неплохо. Однако, бесполезно. Я не вижу пользы даже в 100МБ, если это снижает скорость запуска приложения.

Если размер вашего приложения будет большим, то целесообразнее не упаковывать приложение в .zip и далее действовать по стандартной схеме, а положить файлы приложения (включая package.json) рядом с nw. При запуске nw выполняется поиск package.json файла приложения и, как следствие, его запуск. Нужно также учесть, что на OSX это будет тот же каталог, на одном уровне, с node-webkit.app.

Распространение

Разумеется, NodePad нужно распространять среди других пользователей, чтобы получить хоть какое-то удовлетворение от проделанной работы. Неважно, будет ли это обратная связь по найденным ошибкам, предложение о модификации и улучшении существующего функционала или же вовсе банальное спасибо (не исключен также вариант с гневными отзывами о том, что больше писать приложения лучше не стоит и вообще пора идти в уборщики или по новой заканчивать школу).

Итого, у нас есть несколько путей распространения программы в виде:

  • Исходников проекта.
  • Собранного проекта (скомпилированного, если так угодно), то есть готово к использованию приложения.
  • Установщика.

Давайте подробнее рассмотрим все три способа.

Распространение исходников

Этот вариант я предлагаю сразу отнести в корзину для «неудачных идей». Если рассмотреть все плюсы такого вида распространения, то их ничтожное количество будет несоизмеримо с минусами.

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

Распространение приложения

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

Однако, пользователю вашего приложения придётся самостоятельно копировать все файлы приложения в какую-то директорию, отличную от «Downloads». В противном случае, при следующей чистке ОС возможен вариант того, что ваше приложение полетит в корзину. При этом пользователь должен создать ярлык для быстрого запуска приложения с рабочего стола, иначе, опять таки, ему придётся каждый раз переходить в директорию, где располагается исполняемый файл, и запускать.

Помимо всех этих лишних телодвижений, если приложение утратит актуальность, пользователю придётся самому удалять все файлы приложения:

  • Директория приложения (например, в Windows, такая: c:\Program Files (x86)\NodePad\).
  • Директория рабочих файлов (что-то похожее на: c:\Users\UserName\AppData\Local\NodePad\).
  • Прочие директории, которые необходимы разработчику.
  • Возможно, ключи реестра, если приложению такие были необходимы.

А если ещё есть какие-то зависимые внешние библиотеки, то... ой, даже думать об этом не хочу.

Мне такой вариант не нравится.

Распространение установщика

Пожалуй, это самый оправданный вариант распространения приложения. Рассмотрим кратко его плюсы и минусы:

Плюсы

  • Пользователь загружает только один исполняемый файл, в котором содержится всё необходимое для работы приложения.
  • Загрузил — Установил — Пользуйся.
  • Если приложение потеряло актуальность, то удаление происходит штатным способом, то есть по стандартам ОС, используя настроенный разработчиком инсталлятор.
  • Возможно создание установщика — загрузчика приложения, когда установщик знает откуда загружать последнюю версию приложения.

Минусы

  • Разработчик должен сделать такой установщик и правильно его настроить под каждую ОС.

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

Итоги

Вот и подошёл к концу небольшой цикл статей, в котором мы так упорно создавали наше первое node-webkit приложение. Напомню, что основной целью этого цикла было ознакомление с технологиями создания нативного приложения на Node.js, используя node-webkit.

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

В заключении хочу привести скромный список ресурсов, с которыми вы можете ознакомиться для дальнейшего изучения node-webkit:

Статьи и презентации

Видео

Обязательно обратите внимание на великолепную шпаргалку от Julien Le Coupanec, в которой можно найти уйму информации о node-webkit, буквально на нескольких страницах А4. Если вы серьёзно решите заниматься разработкой на этой платформе, то советую распечатать.

Внимание

Готовое приложение можно найти в репозитории на GitHub по этой ссылке.

UPD: Обновление (1.03.2015)

Для упрощения сборки проекта можно использовать специальную утилиту. Подробнее в мини-обзоре.