はじめに
「デスクトップアプリを作りたいだけなのに、なぜインストーラーが100MBを超えるのか」——Electronでアプリを配布したことがある人なら、一度はこの疑問にぶつかったことがあるはずです。ElectronはChromiumとNode.jsをまるごと同梱するため、どんなに小さなアプリでも重量級になってしまいます。
Neutralinoは、この「重さ」の問題に別の角度から答えを出したフレームワークです。ChromiumもRustツールチェーンも必要とせず、OSに標準搭載されているWebViewをそのまま利用することで、圧縮時0.5MB前後という驚くほど軽量なデスクトップアプリを実現します。
この記事では、Neutralinoの特徴からインストール方法、実際のコード例までを順を追って解説します。
Neutralinoとは
Neutralino(Neutralinojs)は、HTML・CSS・JavaScriptだけでWindows・macOS・Linux向けのクロスプラットフォームデスクトップアプリを構築できる軽量フレームワークです。React・Vue・Svelte・Angularなど好みのフロントエンドフレームワークをそのまま利用でき、既存のWebアプリをほぼそのまま移植することもできます。
Neutralinoの最大の特徴は、Chromiumをバンドルしない設計にあります。ElectronがChromium一式をアプリ内に同梱するのに対し、Neutralinoは各OSに標準搭載されているWebView(WindowsならWebView2、macOSならWebKit、LinuxならWebKitGTK)を利用します。そのため、単純な未圧縮アプリでも約2MB、圧縮すれば0.5MB程度というサイズに収まります。
主な特徴
- 超軽量 - Chromiumを同梱せずOS標準のWebViewを利用するため、アプリサイズが数百KB〜数MBに収まる
- 学習コストが低い - Rustのようなシステムプログラミング言語の知識が不要で、JavaScriptの知識だけで始められる
- ネイティブAPIへのアクセス -
Neutralino.filesystemやNeutralino.osといったAPI経由で、ファイル操作・ネイティブダイアログ・コマンド実行などOS機能を呼び出せる - 柔軟な動作モード - デスクトップアプリとしてだけでなく、クラウドモードやChrome拡張モードでも動作させられる
インストール
まずCLIツールの@neutralinojs/neuをグローバルにインストールします。
npm install -g @neutralinojs/neu
続けて、新規プロジェクトを作成します。
neu create myapp
cd myapp
neu run
neu runで開発用ウィンドウが起動し、neu buildで各OS向けの実行ファイルをビルドできます。ライブラリ単体を既存のWebプロジェクトに組み込みたい場合は、@neutralinojs/libをnpmから追加することも可能です。
npm install @neutralinojs/lib
基本的な使い方
Neutralinoアプリのフロントエンドコードは、Neutralino.init()を呼び出してネイティブプロセスとの通信を開始するところから始まります。以降はNeutralinoというグローバルオブジェクトを通じて、ファイルシステムやOS機能にアクセスします。
// Neutralinoの初期化(アプリ起動時に必ず実行する)
Neutralino.init();
// ウィンドウが閉じられる直前のイベントを購読
Neutralino.events.on('windowClose', () => {
Neutralino.app.exit();
});
// OS標準のメッセージボックスを表示
async function showGreeting() {
await Neutralino.os.showMessageBox(
'こんにちは',
'Neutralinoアプリが起動しました',
'OK',
'INFO'
);
}
showGreeting();
Neutralino.init()が裏側でWebSocket接続を確立し、フロントエンドのJavaScriptからOSのネイティブ機能を呼び出せるようにしています。この設計により、Node.jsのプロセスをアプリに同梱する必要がなくなり、Electronに比べてアプリサイズを大幅に削減できています。
なお、Neutralinoはネイティブのウィンドウとバックグラウンドプロセスを前提とした動作モデルのため、ブラウザ単体で完結する実行サンプル(LiveCodes)は用意できません。実際に試す場合は、上記のneu createからローカル環境で動かしてみてください。
実践的なユースケース
ファイルシステム操作
デスクトップアプリならではの需要として、ローカルファイルの読み書きがあります。Neutralino.filesystemAPIを使うと、ブラウザのセキュリティ制約を気にせずファイルを直接操作できます。
// テキストファイルの書き込みと読み込み
async function saveAndLoad() {
const path = './notes.txt';
await Neutralino.filesystem.writeFile(path, 'Neutralinoで書いたメモです');
const content = await Neutralino.filesystem.readFile(path);
console.log(content); // -> "Neutralinoで書いたメモです"
}
saveAndLoad();
writeFileとreadFileはいずれもPromiseを返すため、async/awaitで自然に扱えます。設定ファイルの永続化やローカルキャッシュの保存など、Webアプリでは実装が面倒だった処理が数行で完結します。
ネイティブダイアログとコマンド実行
Neutralino.osAPIを使うと、OS標準のファイル選択ダイアログや、シェルコマンドの実行が可能になります。CLIツールをラップしたGUIアプリを作る際に特に役立つ機能です。
// ファイル選択ダイアログを開き、選んだファイルパスを取得
async function pickFile() {
const entries = await Neutralino.os.showOpenDialog('ファイルを選択', {
multiSelections: false,
});
return entries[0];
}
// シェルコマンドを実行して結果を受け取る
async function runCommand() {
const info = await Neutralino.os.execCommand('echo Hello from Neutralino');
console.log(info.stdOut); // -> "Hello from Neutralino"
}
showOpenDialogはOSネイティブのダイアログをそのまま呼び出すため、Web標準の<input type="file">よりも柔軟にファイル選択のUIをカスタマイズできます。execCommandと組み合わせれば、既存のCLIツールに簡易的なGUIをかぶせるような使い方もできます。
ウィンドウ制御
Neutralino.windowAPIでは、アプリのウィンドウサイズやタイトル、最小化・最大化といった状態を細かく制御できます。複数ウィンドウを持つアプリを作る際にも活用します。
// ウィンドウのタイトルとサイズを動的に変更
async function customizeWindow() {
await Neutralino.window.setTitle('マイNeutralinoアプリ');
await Neutralino.window.setSize({ width: 800, height: 600 });
}
// 常に最前面に表示する
async function pinToTop() {
await Neutralino.window.setAlwaysOnTop(true);
}
customizeWindow();
これらのAPIはすべて非同期で、ウィンドウの状態変更が完了した時点でPromiseが解決します。ツールバー的な小さなユーティリティアプリを「常に最前面」で表示させたい場合などに、setAlwaysOnTopが役立ちます。
まとめ
Neutralinoは、Chromiumをバンドルしないという発想でElectronの「重さ」という課題に正面から向き合ったフレームワークです。Rustの学習コストを避けたい場合や、既存のシンプルなWebアプリを手早くデスクトップアプリ化したい場合に、有力な選択肢になります。
一方で、OS標準のWebViewに依存するぶん、Chromiumを固定でバンドルするElectronほどの表示の一貫性は保証されません。アプリの性質やターゲット環境に応じて、ElectronやTauriと使い分けるのがよいでしょう。まずはneu createで小さなプロジェクトを作り、Neutralino.filesystemやNeutralino.osのAPIを触ってみることをおすすめします。
