はじめに
JSで単体アプリケーションを作成する際Electronでは重すぎて何か代替案はないかと模索していた。
見つけた。
GIt Hub
neutralinojs
公式
neutralinojs
動作も軽くビルドもアプリケーションの起動もElectronと比較にならない程速い。
制限は色々とあるものの要件にフィットすればマルチプラットフォームなJSデスクトップアプリとしてElectronの代替になりうると思う。
『業務の効率化を図るためチームのメンバにツールを配布』と言った際には十分はまると思う。
検証環境
以下の環境で開発
OS:windows10
node:14.15.4
npm:7:20.1
neutralinojs/neu : 5.2.1
環境構築
以下のコマンドでNeutralinojsをグローバルにインストール(gじゃなくても可)
npm install -g @neutralinojs/neu
アプリケーションの作成
neu create myapp
アプリケーションの実装
作成されたindex.htmlを編集(実行ファイルの重さがどれくらいになるか知りたかったので色々入れてみた)
- ムダにSPAを導入(メニューはettricsさんから頂く)
- ムダにSME(simple markdown editor)を導入しみてた。
- ムダにローディングを導入しみてた。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>NeutralinoJs test</title>
<link id='stylesheet' rel='stylesheet' href='./css/bonsai-base.min.css'>
<link rel="stylesheet" href="./css/page.css">
<link rel="stylesheet" href="./css/styles.css">
</head>
<body>
<div id="neutralinoapp">
<!-- menu -->
<header class="header">
<div class="burger">
<div class="burger__patty"></div>
<div class="burger__patty"></div>
<div class="burger__patty"></div>
</div>
<nav class="menu">
<div class="menu__brand">
<h3>Neutralinojs test</h3>
</div>
<ul class="menu__list">
<li class="menu__item"><a class="menu__link" name="start"></a></li>
<li class="menu__item"><a href="#Page1" class="menu__link" name="part1">Page1</a></li>
<li class="menu__item"><a href="#Page2" class="menu__link" name="part2">Page2</a></li>
<li class="menu__item"><a href="#Page1" class="menu__link" name="part1">Page3</a></li>
<li class="menu__item"><a href="#Page2" class="menu__link" name="part2">Page4</a></li>
</ul>
</nav>
</header>
<!-- 各ページメイン(sectionごと) -->
<section id="start" class="fade">
<h2>Neutralinojs test</h2>
</section>
<section id="Page1" class="fade">
<div class="section-content">
<h3>設定</h3>
<fieldset>
<legend>ヘッダ</legend>
<div class="grid" style="--col: 2">
<p><label>タイトル<input type="text" placeholder="タイトル"></label></p>
<p><label>サブタイトル<input type="text" placeholder="サブタイトル"></label></p>
</div>
<p><label>メインイメージファイル<input type="text" placeholder="メインイメージファイル名"></label></p>
<label>デスクリプション<textarea placeholder="Hello People..."></textarea></label>
</fieldset>
</div>
</section>
<section id="Page2" class="fade">
<div class="section-content">
<textarea id="editor" name="name" rows="8" cols="40"></textarea>
</div>
</section>
<section id="Page3" class="fade">
<div class="section-content">
page3
</div>
</section>
<section id="Page4" class="fade">
<div class="section-content">
page4
</div>
</section>
<!-- ローディング -->
<div class="loading-wrap" style="display:none">
<div class="loader">Loading...</div>
</div>
</div>
<!-- Neutralino.js client. This file is gitignored,
because `neu update` typically downloads it.
Avoid copy-pasting it.
-->
<script src="src/page.js"></script><!-- page control-->
<script src="src/neutralino.js"></script>
<!-- Your app's source files -->
<script src="src/service.js"></script>
<script src="src/controller.js"></script>
<!-- loading -->
<link rel="stylesheet" href="./css/loading.css" />
<!-- Simple MDE(Mark Down Editor) -->
<link rel="stylesheet" href="./css/simplemde.min.css">
<script src="./lib/simplemde.min.js"></script>
<script>var simplemde = new SimpleMDE({ element: document.getElementById("editor") });</script>
</body>
</html>
画面
初期画面
メニュ(ettricsさんのソースをダダぱくり)
入力画面(classlessフレームワークとしてbonsai.css導入)
マークダウンエディタ(SimpleMDE)
アプリケーションの実行
neu run
尚、windowsの場合以下のコマンドの実行が1度だけ必要らしい
CheckNetIsolation.exe LoopbackExempt -a -n="Microsoft.Win32WebViewHost_cw5n1h2txyewy
ビルド
neu build --release
ムダに重くしようと試みましたがdist配下の配布用ファイルの合計は4.5MB。Electronとは2桁違い。比較の対象にならない程軽い!(releaseオプションでできるzipファイルは1MBちょっと)
デバッグ
デバッグ時以下のコマンドで変更をウォッチしてくれます。
またneutralino.config.jsonファイルで"defaultMode": "browser"にすればweb viewでなくブラウザでもデバッグが出来ます(ファイルの読み書きなどしている際は制限有)
neu listen
途中で感じたメリット/デメリットをまとめてみた
メリット
- 環境構築が手軽
- ビルドが速い
- jsの資産が利用できる
- 実行ファイルが異常に軽い
- typescript可
- vueやReact等で画面を作ることも可
デメリット
- データベースが利用できない(代替案としてNeutralino.storageが用意されている、が貧弱)
- requireが使用できないので既存のコードにrequireが入っていれば修正の必要がある
- 短期間で破壊的に変更してくれる
- 製作者がインド人でチュートリアルの英語がひどい
参考1 ファイルの読み書き
/* jsonファイル読込 */
async function readJsonFile(file){
let response = await Neutralino.filesystem.readFile({
fileName: file
});
return JSON.parse(response.data);
}
/* jsonファイル書込 */
async function writeJsonFile(fileName,fileContents,message){
await Neutralino.filesystem.writeFile({
fileName: fileName,
data: JSON.stringify(fileContents),
});
}
参考2 ストレージ
以下公式そのままだがLocalStorage感覚でNeutralinoが管理するストレージにデータを保管することが出来る
/* データの保存 */
await Neutralino.storage.putData({
bucket: 'userDetails',
data: JSON.stringify({
username: 'TestValue'
})
});
/* データの取得 */
let response = await Neutralino.storage.getData({
bucket: 'userDetails'
});