Розробка десктоп-приложення на 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');
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.forEach(entry => {
const item = document.createElement('div');
item.className = `file-item ${entry.isDirectory() ? 'dir' : 'file'}`;
item.textContent = 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);
Сборка дистрибутива
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',
macIcns: './icons/icon.icns',
winIco: './icons/icon.ico'
});
nw.build().then(() => {
console.log('Build complete');
}).catch(console.error);
Версії NW.js: Normal vs SDK
NW.js доступний у двох версіях:
- Normal — для production, без DevTools за замовчуванням, менший розмір
- SDK — з DevTools, для розробки та відлагодження
Коли NW.js має смисл
Розглядайте NW.js для: портування існуючого веб-приложення без переробки архітектури, швидких внутрішніх інструментів де security model менш критична, проектів де команда раніше працювала з node-webkit.
Для нових production-приложень Electron або Tauri мають більш активне сообщество, кращу документацію та більш передбачувану модель безпеки.







