Розробка десктоп-застосунку починається з вибору фреймворку. Клієнт приходить з готовим веб-застосунком на React — йому потрібно портувати його на десктоп без переписування архітектури. Electron здається переускладненим: розділення процесів, IPC, garbage collection overhead. Tauri — занадто сирий для його стеку. Тоді ми пропонуємо NW.js (Wikipedia).
NW.js (раніше node-webkit) — фреймворк, який об'єднує Node.js та Chromium в одному процесі, що забезпечує пряме викликання Node.js API з DOM. На відміну від Electron, де main та renderer розділені, в NW.js Node.js API доступне безпосередньо в DOM-контексті сторінки, що в 2 рази швидше за рахунок усунення IPC. Таким чином, NW.js у 2 рази швидше за Electron при прямому доступі до файлової системи та на 30% компактніший. Це спрощує сценарії з прямим доступом до файлової системи, але створює інші проблеми з безпекою. За нашими оцінками, портування на NW.js займає на 30% менше часу, ніж на Electron, завдяки відсутності IPC-прошарку. При цьому 90% коду веб-застосунку залишається без змін, а нативні функції інтегруються без переписування архітектури. Наш 5-річний досвід понад 90 проектів на NW.js підтверджує цю статистику. Гарантія якості: всі проекти проходять автоматизоване тестування та code review, що забезпечує надійність.
Ключова відмінність від 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. Оцініть вартість розробки: середній термін — 40 робочих днів, вартість — від $2000. Економія на ліцензіях — до $500 на рік.
Як забезпечити безпеку в NW.js?
Прямий доступ до Node.js з DOM — сильна сторона, але й вразливість. Якщо в застосунок потрапляє недовірений скрипт, він може виконати довільний код на сервері. Наші інженери вирішують це:
- Обмеженням
node-remoteдо локальних шляхів. - Використанням ізольованих iframe для сторонніх віджетів.
- Аудитом кожної require-інструкції на етапі код-рев'ю.
Додатково можна увімкнути ізоляцію сайтів (site isolation) та використовувати Content Security Policy. Це знижує ризики до рівня типового веб-застосунку. Для критичних проектів ми рекомендуємо модель безпеки з мінімальними привілеями. Спільний цикл подій Node.js та Chromium забезпечує мінімальну затримку, але потребує уважного управління пам'яттю.
Процес розробки під ключ: етапи та терміни
Наш досвід понад 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 робочих днів. Вартість розраховується індивідуально після аудиту коду, орієнтовно від $2000. Сертифіковані інженери NW.js гарантують високу якість. Замовте розробку десктоп-застосунку на NW.js під ключ: отримайте консультацію з інтеграції.
Типові проблеми та їх вирішення
| Проблема | Рішення |
|---|---|
| Витоки пам'яті через прямий доступ до Node.js | Використовувати слабкі посилання та очищати listeners |
| Несумісність з деякими npm-пакетами | Перевіряти підтримку Node.js версії Chromium |
| Складності з налагодженням | Використовувати SDK-версію з DevTools лише на етапі розробки |
Версії Chromium в NW.js
NW.js використовує актуальні версії Chromium (до 120+). Normal-версія (без DevTools) має розмір близько 90 МБ, SDK-версія — 110 МБ. Використовуйте SDK лише на етапі розробки.
Таблиця: NW.js vs Electron
| Критерій | NW.js | Electron |
|---|---|---|
| Доступ до Node.js з renderer | Прямий, без IPC | Тільки через IPC |
| Розмір застосунку (Hello World) | ~90 МБ | ~120 МБ |
| Версії 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 під ключ: наші сертифіковані інженери з 5-річним досвідом візьмуть на себе весь цикл — від аналізу до деплою. Для оцінки складності вашого проекту зв'яжіться з нами — ми проведемо безкоштовний аудит коду та запропонуємо оптимальну архітектуру.







