JS単体アプリケーションにNeutralinoを検討してみる

### はじめに
JSで単体アプリケーションを作成する際[Electron](https://www.electronjs.org/)では重すぎて何か代替案はないかと模索していた。
見つけた。

GIt Hub
[neutralinojs](https://github.com/neutralinojs/neutralinojs)

公式
[neutralinojs](https://neutralino.js.org)


動作も軽くビルドもアプリケーションの起動も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さん](https://codepen.io/ettrics/pen/JoaaxW)から頂く）
- ムダにSME(simple markdown editor)を導入しみてた。
- ムダにローディングを導入しみてた。

``` html
<!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>

```

### 画面
##### 初期画面
![welcome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151893/80398502-b8c9-ad71-635b-10b5a5a1b2b3.png)
##### メニュ([ettricsさん](https://codepen.io/ettrics/pen/JoaaxW)のソースをﾀﾞﾀﾞぱくり)
![mennu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151893/751de5f2-bf58-6a74-58f2-88fb629ee00c.png)
##### 入力画面(classlessフレームワークとして[bonsai.css](https://www.bonsaicss.com)導入)
![input.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151893/9337cbb1-96b5-4bb3-7cd3-5d50e34ae90c.png)
##### マークダウンエディタ([SimpleMDE](https://simplemde.com))
![sme.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151893/3618340c-4bb6-0165-3e4a-ec2279ef861e.png)


### アプリケーションの実行
```
neu run
```

尚、windowsの場合以下のコマンドの実行が1度だけ必要らしい

```
CheckNetIsolation.exe LoopbackExempt -a -n="Microsoft.Win32WebViewHost_cw5n1h2txyewy
```

#### ビルド
```
neu build --release
```

ムダに重くしようと試みましたがdist配下の配布用ファイルの合計は4.5MB。Electronとは2桁違い。比較の対象にならない程軽い！（releaseオプションでできるzipファイルは1MBちょっと）
![2021-08-15_142648.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151893/1e98f38b-8bdd-19c9-30c0-60cef778e168.jpeg)

#### デバッグ
デバッグ時以下のコマンドで変更をウォッチしてくれます。
またneutralino.config.jsonファイルで"defaultMode": "browser"にすればweb viewでなくブラウザでもデバッグが出来ます（ファイルの読み書きなどしている際は制限有）

```
neu listen
```

### 途中で感じたメリット/デメリットをまとめてみた
#### メリット
- 環境構築が手軽
- ビルドが速い
- jsの資産が利用できる
- 実行ファイルが異常に軽い
- typescript可
- vueやReact等で画面を作ることも可

#### デメリット
- データベースが利用できない（代替案としてNeutralino.storageが用意されている、が貧弱）
- requireが使用できないので既存のコードにrequireが入っていれば[修正の必要](https://github.com/neutralinojs/neutralinojs/issues/425)がある
- 短期間で破壊的に変更してくれる
- 製作者がインド人でチュートリアルの英語がひどい

#### 参考1 ファイルの読み書き
``` js
/* 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が管理するストレージにデータを保管することが出来る

```js
/* データの保存 */
await Neutralino.storage.putData({
  bucket: 'userDetails',
  data: JSON.stringify({
    username: 'TestValue'
  })
});
/* データの取得 */
let response = await Neutralino.storage.getData({
  bucket: 'userDetails'
});
```