Разработка десктоп-приложения на NW.js

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка десктоп-приложения на NW.js
Средняя
~1-2 недели
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1229
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1166
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    863
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1075
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    829
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    839

Разработка десктоп-приложения на NW.js

NW.js (ранее node-webkit) — фреймворк для десктоп-приложений, объединяющий Node.js и Chromium в одном процессе. В отличие от Electron, где main и renderer разделены, в NW.js Node.js API доступно прямо в DOM-контексте страницы. Это упрощает некоторые сценарии, но создаёт другие проблемы с безопасностью.

Ключевое отличие от Electron

В Electron: renderer → IPC → main → Node.js API. В NW.js: renderer напрямую вызывает require('fs'), require('path') и т.д.

// В NW.js это работает прямо в браузерном коде
const fs = require('fs');
const os = require('os');

document.getElementById('hostname').textContent = os.hostname();
fs.readFile('/etc/hosts', 'utf8', (err, data) => {
  document.getElementById('hosts').textContent = data;
});

Для небольших утилитарных приложений это действительно удобнее. Для сложных приложений — сложнее рассуждать о безопасности.

Создание проекта

npm init -y
npm install nw --save-dev  # или nw-builder для production сборок
{
  "name": "my-nw-app",
  "main": "index.html",
  "window": {
    "title": "My Application",
    "width": 1200,
    "height": 800,
    "min_width": 800,
    "min_height": 600,
    "icon": "icons/icon.png",
    "frame": true,
    "resizable": true
  },
  "nodejs": true,
  "node-remote": "",
  "chromium-args": "--enable-features=WebRTC-H264WithOpenH264FFmpeg"
}

Поле main в package.json — точка входа, здесь это HTML-файл, а не JS.

Структура проекта

my-app/
├── package.json     # конфигурация NW.js
├── index.html       # главное окно
├── js/
│   ├── app.js       # логика приложения
│   └── native.js    # Node.js интеграция
├── css/
│   └── style.css
└── icons/
    └── icon.png

Пример: файловый менеджер

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>File Browser</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <div id="toolbar">
    <button id="btn-open">Открыть папку</button>
    <span id="current-path"></span>
  </div>
  <div id="file-list"></div>
  <script src="js/app.js"></script>
</body>
</html>
// js/app.js — Node.js API прямо в renderer
const fs = require('fs');
const path = require('path');

// NW.js предоставляет nw объект в глобальном контексте
const { dialog } = nw;

let currentPath = require('os').homedir();

function renderFiles(dirPath) {
  document.getElementById('current-path').textContent = dirPath;
  const list = document.getElementById('file-list');
  list.innerHTML = '';

  try {
    const entries = fs.readdirSync(dirPath, { withFileTypes: true });
    entries.sort((a, b) => {
      if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
      return a.name.localeCompare(b.name);
    });

    entries.forEach(entry => {
      const item = document.createElement('div');
      item.className = `file-item ${entry.isDirectory() ? 'dir' : 'file'}`;
      item.textContent = entry.name;
      item.addEventListener('dblclick', () => {
        if (entry.isDirectory()) {
          currentPath = path.join(dirPath, entry.name);
          renderFiles(currentPath);
        } else {
          nw.Shell.openItem(path.join(dirPath, entry.name));
        }
      });
      list.appendChild(item);
    });
  } catch (err) {
    list.innerHTML = `<div class="error">${err.message}</div>`;
  }
}

document.getElementById('btn-open').addEventListener('click', () => {
  const input = document.createElement('input');
  input.type = 'file';
  input.setAttribute('nwdirectory', ''); // NW.js расширение для выбора папки
  input.addEventListener('change', () => {
    currentPath = input.value;
    renderFiles(currentPath);
  });
  input.click();
});

renderFiles(currentPath);

Нативное меню

// js/menu.js
const menu = new nw.Menu({ type: 'menubar' });

const fileMenu = new nw.Menu();
fileMenu.append(new nw.MenuItem({
  label: 'Открыть',
  key: 'o',
  modifiers: 'ctrl',
  click: () => openFolder()
}));
fileMenu.append(new nw.MenuItem({ type: 'separator' }));
fileMenu.append(new nw.MenuItem({
  label: 'Выход',
  key: 'q',
  modifiers: 'ctrl',
  click: () => nw.App.quit()
}));

menu.append(new nw.MenuItem({ label: 'Файл', submenu: fileMenu }));

// Применить к окну (только macOS)
if (process.platform === 'darwin') {
  nw.Window.get().menu = menu;
}

Системный трей

const tray = new nw.Tray({
  title: 'My App',
  icon: 'icons/tray-16.png',
  iconsAreTemplates: false // macOS
});

const trayMenu = new nw.Menu();
trayMenu.append(new nw.MenuItem({
  label: 'Показать',
  click: () => nw.Window.get().show()
}));
trayMenu.append(new nw.MenuItem({
  label: 'Выход',
  click: () => nw.App.quit()
}));

tray.menu = trayMenu;
tray.on('click', () => {
  const win = nw.Window.get();
  win.isVisible() ? win.hide() : win.show();
});

Сборка дистрибутива

# Установка nw-builder
npm install nw-builder --save-dev
// build.js
const NwBuilder = require('nw-builder');

const nw = new NwBuilder({
  files: ['./src/**/**', './package.json'],
  version: '0.89.0',
  platforms: ['win64', 'osx64', 'linux64'],
  buildDir: './release',
  buildType: 'default',
  macIcns: './icons/icon.icns',
  winIco: './icons/icon.ico',
  zip: true
});

nw.build().then(() => {
  console.log('Build complete');
}).catch(console.error);

Версии NW.js: Normal vs SDK

NW.js доступен в двух версиях:

  • Normal — для production, без DevTools по умолчанию, меньше размер
  • SDK — с DevTools, для разработки и отладки
{
  "devDependencies": {
    "nw": "0.89.0-sdk"
  }
}

Когда NW.js имеет смысл

NW.js стоит рассмотреть для: портирования существующего веб-приложения без переработки архитектуры, быстрых внутренних инструментов где security model менее критична, проектов где команда работала с node-webkit раньше и знает его особенности.

Для новых production-приложений Electron или Tauri имеют более активное сообщество, лучшую документацию и более предсказуемую модель безопасности.