Разработка десктоп-приложения начинается с выбора фреймворка. Клиент приходит с готовым веб-приложением на 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 в ваш стек. Мы подберём оптимальную архитектуру и сроки. Для оценки сложности вашего проекта свяжитесь с нами — мы проведем бесплатный аудит кода и предложим оптимальную архитектуру. Закажите разработку под ключ: наши инженеры возьмут на себя весь цикл — от анализа до деплоя.







