Создание настольного ПО с помощью 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 вне зависимости от фокуса выполняется действие (например, показать главное окно).