Создание настольного ПО с помощью NW.js

Раздел: Разработка -> Десктопные фреймворки

Основной способ создания приложения на NW.js

Как быстро запустить десктопное приложение, используя HTML и JavaScript?

Для работы с NW.js требуется скачать бинарную сборку с официального сайта (nwjs.io). После распаковки создаются файлы проекта: package.json и index.html. В package.json указывается точка входа (главный HTML файл) и настройки окна. Затем в терминале выполняется команда nw . из папки проекта.

{
  "name": "demo",
  "main": "index.html",
  "window": {
    "width": 800,
    "height": 600,
    "title": "Demo App"
  }
}

Nw js (nw.js (node-webkit) для десктопных приложений)

<!DOCTYPE html>
<html>
<head>
  <title>Demo App</title>
</head>
<body>
  <h1>Hello NW.js</h1>
</body>
</html>

При запуске открывается окно с указанными размерами и содержимым HTML. Все Node.js модули становятся доступны через require в JavaScript, встроенном в страницу.

Частая проблема: окно не открывается или появляется консоль с ошибкой о невозможности загрузить package.json. Решение: проверить, что package.json находится в корне папки, из которой запускается nw, и что он синтаксически корректен (валидный JSON).

Вариант 1: доступ к нативным диалогам через модуль nw.Dialog

Как открыть диалог выбора файла или папки?

Модуль nw.Dialog предоставляет методы для открытия нативных диалоговых окон. Пример вызова диалога открытия файла:

const nw = require('nw.gui');
nw.Dialog.open({
  filters: [{ name: 'Текстовые файлы', extensions: ['txt'] }]
}, (filePath) => {
  if (filePath) console.log('Выбран файл:', filePath);
});

Ошибка: диалог может не появиться, если вызов происходит до события 'open' окна. Рекомендуется запускать диалог после загрузки страницы (DOMContentLoaded).

Вариант 2: использование системного буфера обмена

Как скопировать текст в буфер обмена и прочитать из него?

Модуль nw.Clipboard предоставляет доступ к системному буферу обмена.

const clipboard = nw.Clipboard.get();
clipboard.set('Текст для копирования', 'text');
const readText = clipboard.get('text');
console.log(readText);

Проблема: на некоторых Linux системах может отсутствовать поддержка буфера обмена. Решение: проверять, что clipboard.get() не возвращает null.

Вариант 3: работа с дочерними процессами через child_process

Как выполнить системную команду и получить её вывод?

NW.js имеет доступ ко всем Node.js модулям, включая child_process.

const { exec } = require('child_process');
exec('ls -la', (error, stdout, stderr) => {
  if (error) console.error(stderr);
  else console.log(stdout);
});

Типичная ошибка: команда не найдена из-за разных путей в packaged приложении. Решение: указывать полный путь к исполняемому файлу или использовать which для поиска.

Вариант 4: настройка упаковки приложения в один исполняемый файл

Как собрать приложение для распространения без необходимости установки NW.js отдельно?

Для упаковки используется инструмент nwjs-builder-phantom или вручную копирование файлов в папку с nw и переименование. Простейший способ: создать архив ZIP всех файлов проекта, переименовать его в .nw и поместить в папку с nw.exe.

// Команды для сборки (пример для Windows)
// Создать архив myapp.nw из всех файлов проекта
// Затем скопировать его в папку с nw.exe и запустить: copy /b nw.exe+myapp.nw myapp.exe

Проблема: после упаковки приложение теряет доступ к файловой системе по относительным путям. Решение: использовать process.cwd() или __dirname в сочетании с require для нахождения ресурсов.

Расширенный пример: создание системного трея с иконкой и всплывающим меню.

Пример
const nw = require('nw.gui');
const tray = new nw.Tray({ icon: 'icon.png', title: 'Мое приложение' });
const menu = new nw.Menu();
menu.append(new nw.MenuItem({ label: 'Показать окно', click: () => nw.Window.get().show() }));
menu.append(new nw.MenuItem({ type: 'separator' }));
menu.append(new nw.MenuItem({ label: 'Выход', click: () => nw.App.quit() }));
tray.menu = menu;
// При клике на иконку (левой кнопкой) можно показать окно
tray.on('click', () => nw.Window.get().show());
Иконка приложения отображается в области уведомлений. Левая кнопка показывает окно, правая вызывает контекстное меню.

Расширенный пример: межоконное взаимодействие с помощью postMessage и nw.Window.open.

Пример
// В главном окне
const mainWin = nw.Window.get();
const secondWin = nw.Window.open('second.html', { width: 400, height: 300 });
secondWin.on('loaded', () => {
  secondWin.window.postMessage({ type: 'greeting', text: 'Привет из первого окна!' }, '*');
});
// В second.html
window.addEventListener('message', event => {
  const data = event.data;
  if (data.type === 'greeting') {
    document.getElementById('msg').textContent = data.text;
  }
});
Второе окно получает сообщение и отображает текст.

Расширенный пример: автоматическое обновление приложения с помощью проверки версии из сети.

Пример
const http = require('http');
const { app } = require('nw.gui');
const currentVersion = app.manifest.version;
http.get('http://example.com/version.json', (res) => {
  let data = '';
  res.on('data', chunk => data += chunk);
  res.on('end', () => {
    const latest = JSON.parse(data).version;
    if (latest !== currentVersion) {
      // уведомление пользователю
      alert('Доступна новая версия: ' + latest);
    }
  });
});
При запуске проверяется версия на сервере, если отличается - показывается уведомление.

Расширенный пример: использование WebSocket для связи между окнами или с сервером.

Пример
// В главном окне
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', ws => {
  ws.on('message', msg => {
    console.log('Получено:', msg);
    // Разослать всем клиентам
    wss.clients.forEach(client => {
      if (client.readyState === WebSocket.OPEN) client.send(msg);
    });
  });
});
// В любом окне приложения
const socket = new WebSocket('ws://localhost:8080');
socket.onopen = () => socket.send('Привет от второго окна');
Создается локальный WebSocket-сервер, окна могут обмениваться данными в реальном времени.

Расширенный пример: добавление глобальных горячих клавиш.

Пример
const nw = require('nw.gui');
const shortcut = new nw.Shortcut({ key: 'Ctrl+Shift+A' });
shortcut.on('active', () => {
  console.log('Горячая клавиша сработала');
  // Показать окно
  nw.Window.get().show();
});
nw.App.registerGlobalHotKey(shortcut);
При нажатии Ctrl+Shift+A вне зависимости от фокуса выполняется действие (например, показать главное окно).

NW.js (Node-Webkit) для десктопных приложений - comments

En
Nw js (javascript)