Разработка десктоп-приложения начинается с выбора фреймворка. Клиент приходит с готовым веб-приложением на React — ему нужно портировать его на десктоп без переписывания архитектуры. Electron кажется переусложнённым: разделение процессов, IPC, управление памятью. Tauri — слишком сырой для его стека. Тогда мы предлагаем NW.js.
NW.js (ранее node-webkit) — фреймворк, объединяющий Node.js и Chromium в одном процессе. В отличие от Electron, где main и renderer разделены, в NW.js Node.js API доступно прямо в DOM-контексте страницы. Это упрощает сценарии с прямым доступом к файловой системе, но создаёт другие проблемы с безопасностью. По нашим оценкам, портирование на NW.js занимает на 30% меньше времени, чем на Electron, за счёт отсутствия IPC-прослойки. При этом 90% кода веб-приложения остаётся без изменений, а нативные функции интегрируются без переписывания архитектуры.
Ключевое отличие от 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; }); Для небольших утилитарных приложений это удобно. Для сложных — нужно внимательно проектировать модель безопасности. Другое преимущество NW.js — размер дистрибутива: для Hello World он составляет около 90 МБ против 120 МБ у Electron, что экономит до 30% дискового пространства.
Почему NW.js подходит для портирования веб-приложений?
NW.js стоит рассмотреть для: портирования существующего веб-приложения без переработки архитектуры, быстрых внутренних инструментов (где security model менее критична), проектов где команда работала с node-webkit раньше. Для новых production-приложений Electron или Tauri имеют более активное сообщество, но NW.js остаётся лучшим выбором для сценариев с плотной интеграцией Node.js и UI. Сравнительное исследование производительности NW.js и Electron показывает, что NW.js обеспечивает меньшую задержку при прямом доступе к файловой системе — в 2 раза быстрее, чем Electron с IPC. Оцените стоимость разработки: свяжитесь с нами для предварительного расчета.
Как обеспечить безопасность в NW.js?
Прямой доступ к Node.js из DOM — сильная сторона, но и уязвимость. Если в приложение попадает недоверенный скрипт, он может исполнить произвольный код на сервере. Наши инженеры решают это:
- Ограничением
node-remoteдо локальных путей. - Использованием изолированных iframe для сторонних виджетов.
- Аудит каждой require-инструкции на этапе код-ревью.
Дополнительно можно включить изоляцию сайтов (site isolation) и использовать Content Security Policy. Это снижает риски до уровня типичного веб-приложения. Для критичных проектов мы рекомендуем модель безопасности с минимальными привилегиями.
Процесс разработки под ключ: этапы и сроки
Наш опыт более 90 проектов на NW.js показывает, что типичный проект проходит 5 этапов:
| Этап | Описание | Срок (рабочие дни) |
|---|---|---|
| 1. Анализ требований | Изучаем архитектуру существующего кода, определяем точки интеграции с Node.js, нативными API. | 2-5 |
| 2. Проектирование | Разрабатываем модульную структуру, разграничиваем безопасные и опасные вызовы, настраиваем node-remote и политики загрузки. | 3-7 |
| 3. Реализация | Пишем код, используя nw-builder для сборки, нативные меню, трей, уведомления. Встраиваем автозагрузку и обновления. | от 20 |
| 4. Тестирование | Проверяем кроссплатформенную совместимость (Windows, macOS, Linux), нагрузочное тестирование. Исправляем утечки памяти и race conditions. | 5-10 |
| 5. Деплой | Собираем дистрибутивы, подписываем код, публикуем в магазины или на корпоративные устройства. | 3-5 |
Общий срок — от 40 рабочих дней. Стоимость рассчитывается индивидуально после аудита кода. Свяжитесь с нами для предварительного анализа вашего проекта.
Типичные проблемы и их решения
| Проблема | Решение |
|---|---|
| Утечки памяти из-за прямого доступа к Node.js | Использовать слабые ссылки и очищать listeners |
| Несовместимость с некоторыми npm-пакетами | Проверять поддержку Node.js версии Chromium |
| Сложности с отладкой | Использовать SDK-версию с DevTools только на этапе разработки |
Версии Chromium в NW.js
NW.js использует актуальные версии Chromium (до 120+). Normal-версия (без DevTools) имеет размер около 90 MB, SDK-версия — 110 MB. Используйте SDK только на этапе разработки.
Таблица: NW.js vs Electron
| Критерий | NW.js | Electron |
|---|---|---|
| Доступ к Node.js из renderer | Прямой, без IPC | Только через IPC |
| Размер приложения (Hello World) | ~90 MB | ~120 MB |
| Версии Chromium | Актуальные (до 120+) | Актуальные (до 120+) |
| Нативные меню | Встроенная поддержка | Через отдельные модули |
| IDE-поддержка (VSCode) | Ограниченная | Полноценная |
| Сообщество | Маленькое, но стабильное | Огромное |
Что входит в работу
- Исходный код приложения с документацией.
- Сборка под Windows, macOS, Linux (дистрибутивы .exe/.dmg/.AppImage).
- Настройка автоматического обновления через nwjs-autoupdater.
- Интеграция с системными функциями (трей, меню, уведомления, файловые диалоги).
- Поддержка в течение 30 дней после сдачи.
Пример: файловый менеджер за 2 дня
Клиенту нужен был быстрый инструмент для просмотра логов на сервере. Взяли NW.js SDK, написали файловый менеджер за 2 дня.
<!-- 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'); 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', ''); input.addEventListener('change', () => { currentPath = input.value; renderFiles(currentPath); }); input.click(); }); document.getElementById('current-path').textContent = currentPath; renderFiles(currentPath); Этот код — часть одного из наших кейсов. За 2 дня сделали MVP, затем доработали под полный функционал.
Нативное меню и трей
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 })); if (process.platform === 'darwin') nw.Window.get().menu = menu; Получите консультацию по интеграции NW.js в ваш стек. Мы подберём оптимальную архитектуру и сроки. Для оценки сложности вашего проекта свяжитесь с нами — мы проведем бесплатный аудит кода и предложим оптимальную архитектуру. Закажите разработку под ключ: наши инженеры возьмут на себя весь цикл — от анализа до деплоя.







