見出し画像

【備忘録】Electronでめちゃくちゃ簡単なアプリを作る(環境構築からインストーラーの作成まで)だらだら

HTMLとJavascriptくらいしかまともに書けないので、それだけでネイティブアプリが作れるElectronが大好きです ちゅ

ただやらないとすぐに忘れる(カス)ので備忘録を書いていきますょ

手順を細かく書きすぎたのでめちゃ見にくくなりました

1. かなり初歩から環境構築(Node.js、npmの導入)

上級者の仲間入りをしたいのでLinux使おうと思ってましたが

Windowsを使います(Windowsがかなり好きなため)


まずJavascript実行環境のNode.jsとパッケージ管理ツールのnpmをインストールします

2種類ありますがとりあえずLTS版を引っ張ってきます(サポートが長いとうれしいので)

画像1

順当に進めていくとnpmも自動でインストールされます

インストールが終わると自動的にPATHも通るのでPowerShellとかから使えるようになります うひょ~

画像2

インストール、おしまい

2. プロジェクトの準備(初期化、必要パッケージのインストール)

次にアプリ用に用意したフォルダでプロジェクトの初期化をやります

画像3

npm init

質問(?)に答えていくと下みたいな package.json ファイルが生成されます

{
 "name": "test",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
   "test": "echo \"Error: no test specified\" && exit 1"
 },
 "author": "Bistalink",
 "license": "ISC"
}

上の場合 package.json と同じ階層にある index.js がエントリポイント(最初に読み込まれるファイル)になります

プロジェクトの初期化が終わったらElectron本体とアプリのパッケージングに必要なモジュール(今回はelectron-builder)を入れます

npm install -D electron 
npm install -D electron-builder

2つとも最終的に配布可能な形としてビルドした後は要らないので-D(もしくは--save-dev)オプションをつけて開発用依存なのを明示しておきます(しておくと最終的にこいつらを含まない状態でパッケージングしてくれるっぽい、便利)

※ グローバルインストールしてないのは個人的に特定のプロジェクト用のモジュールは精神衛生上切り離しておきたいってのがあるせいです(語彙力)

3. Electronのしくみ

だいぶ今更ですが一応自分がボケたときのために書いておくとElectronにはメインプロセスレンダラープロセスなるものがあって、それぞれ

メインプロセス:レンダラープロセス(後述)の管理・メニュー・タスクトレイアイコンの設定などいかにも「OSの上で動く"アプリ"としての作業」みたいなのを任されてる

レンダラープロセス:Webページを表示するやつ、ほぼブラウザそのもの

みたいな役割分担をしてます

メインプロセスではNode.jsの機能(ファイルの読み書きなど)に直接アクセスできますがレンダラープロセスでは別途設定をしないとNode.jsの機能は基本的に使えません(悪意のあるページが読み込まれた時にOSの機能が使い放題だと普通にめちゃくちゃ危ないため)

レンダラープロセスは普通に使う分には完全にただのブラウザって感じです(Electron自体がChromiumベースなので仕様もほぼ一緒)


アプリの起動順序・構造としては

1. メインプロセスの起動(エントリポイントの読み込み)

2. メインプロセスからレンダラープロセス(ブラウザ画面)の作成

3. メインプロセスからブラウザ画面にURLもしくはローカルファイルを指定してWebページを読み込む

4. 必要に応じて両者間でやりとりを行う(IPC通信 = プロセス間通信

みたいなかんじになります

4. メインプロセスのコードを書く

さっきの index.js ってやつです とりあえず基本的なやつだけ書きます

const {app, BrowserWindow} = require("electron");

let mainWindow;

const createMainWindow = () => {    // BrowserWindowをインスタンス化する関数(既定ではインスタンス化と同時にウィンドウが開く)
   mainWindow = new BrowserWindow();
};

app.on("ready", ()=> {    // アプリの初期化が終了したらmainWindowを作成する
   createMainWindow();
});

app.on("window-all-closed", () => {    // すべてのウィンドウが閉じたらアプリを終了(Windowsのみ)
   app.quit();
});

プロジェクトを実行するには、package.json がある階層で 

npx electron .

を実行します(今回はelectronをローカルインストールしたのでnpxを使ってます)

画像4

空のブラウザが表示されるやつが完成したので

次にここに表示するWebページを作ります

5. Webページの作成(HTML、Javascript)

なんか適当につくります

<!DOCTYPE html>
<html>
   <head>
       <meta charset="UTf-8">
       <title>テスト</title>
   </head>
   <body>
       <h1>質問来てた</h1>
       <button id="button">結果</button>
       <script>
           const button = document.getElementById("button");    // ボタンを取得
           button.addEventListener("click", () => {    // クリックのイベントリスナを登録
               alert("死刑になる場合と、ならない場合がある");    // Javascript弁護士
           });
       </script>
   </body>
</html>

index.html とか名前を付けて今回は参照しやすいように index.js と同じ場所に置いときます

6. Webページの読み込み・オプション

index.html をブラウザ画面にロードさせるために、さっき作った index.js にちょっと追記します

const {app, BrowserWindow} = require("electron");
const path = require("path");     // 追記

let mainWindow;

const createMainWindow = () => {
   mainWindow = new BrowserWindow({    // 追記:ブラウザ画面に関するオプション
       width: 500,
       height: 500
   });

   mainWindow.loadFile(path.join(__dirname, "index.html"));    // 追記:mainWindowにindex.htmlをロード
};

app.on("ready", ()=> {
   createMainWindow();
});

app.on("window-all-closed", () => {
   app.quit();
});

BrowserWindowはインスタンス化するときに引数にoptionsオブジェクトを渡すことでブラウザ画面に関するいろんなオプションを指定できます(ここでは画面の大きさ指定をしてます)

実行するとこんな感じになります

画像5

クリックすると、死刑になる場合と、ならない場合があります

7. アプリのパッケージング

今のままではNode.jsがインストールされている環境でしか実行できないのでこれを配布できる形にしてzipにまとめたりインストーラーにしたりしていきます

メタデータ(アイコンとか説明文とか会社名とか)を細かく設定したいときは package.json に新たに値を追記する必要がでてくるっぽい(めんどくさいのでやらない)

せめてアイコンは欲しいのでペイントとかで256x256の絵をかいてicon.pngという名前で (プロジェクトのフォルダ)/build/icon.png の位置に保存しておきます(明記されてないときここがデフォルトで参照される)

画像6

高品質なアイコンが良い場合は普通に.ico形式(Windows)でマルチアイコンを作ることになると思いま

ターミナルを開いて

npx electron-builder --win --x64

と入力して実行します

今回の場合はプロジェクトフォルダ内に dist フォルダが新規作成されWindows向け64bit版実行ファイル(+その他いろいろ)が出力されます

画像7

(Setupあーだこーだ) .exe :nsisベースの簡易インストーラ(簡易なのでウィザード画面とかついてないけどちゃんとアンインストールとかにも対応している 偉い)

win-unpacked:実行ファイル本体と必要なファイル群が入ってるフォルダ(zip形式で配布したいときとかはこっちを使うとお便利)

.icon-ico:しらん

画像8

↑ win-unpacked フォルダ内(インストールされたときも最終的にはこいつらが展開される)

package.json にオプションをいろいろ書くと出力形式をmsi(Windows用の標準インストーラー形式)にしたりメタデータの編集したりできるっぽい

おしまいです 終了

体を大事にしてください

おまけ1 ブラウザの真っ白画面の対策

BrowserWindowにページを指定して普通にロードするだけだとページの読み込みが完了するまでの間真っ白なブラウザ画面が表示されることになります

重いページとかになると使う側としては読み込んでいるのか応答していないのかわからない(というか普通に何も表示されてないウィンドウがそこに"在る"というのがあんまり好きじゃない)のでここを改善します

const {app, BrowserWindow} = require("electron");
const path = require("path");

let mainWindow;

const createMainWindow = () => {
   mainWindow = new BrowserWindow({
       show: false    // 追記:インスタンス化した際にWindowを表示しない
   });

   mainWindow.loadFile(path.join(__dirname, "index.html"));
   mainWindow.on("ready-to-show", ()=> {    // 追記:ページの準備が出来たら↓
       mainWindow.show();    // 追記:ウィンドウを表示する
   })
};

app.on("ready", ()=> {
   createMainWindow();
});

app.on("window-all-closed", () => {
   app.quit();
});

他にも ready-to-show イベントを活用するとスプラッシュスクリーンとかも作れるようになります

おまけ2 ソースコードについて

実行に必要なファイル群の中に resources フォルダというのがあってその中に app.asar というファイルが入ってるんですが(場合によってはappフォルダになってる)

asarファイルは文字通りasarコマンドを使うことで解凍することができ、実はこの中にアプリ画面の作成に使ったHTMLファイルとかエントリポイントのjsファイルとか package.json とかソースコードが丸々入ってます(ん?)

HTMLとかJavascriptは実行時に解釈されるってことを考えると生のファイルが入っているのは当たり前っちゃ当たり前なのですが、他人にソースコードを読まれたくないときもあります エッチなソースコードとか(ない)

その際はツール等を使ってファイル自体を難読化しておく必要があります(大抵サイズも増大してロード時間に影響が出るのでやりすぎに注意)

難読化が重要な理由としてもう1つ、Electronは実行時 resources フォルダ内の app.asar もしくは app フォルダを参照しに行くので appフォルダを新規作成しapp.asarの中身を展開→元のapp.asarを削除→appフォルダ内のファイルを編集 みたいなことをすると簡単にアプリの改造が出来るようになってしまいます(実行ファイルがappフォルダ内の改変されたリソースを参照しに行ってしまうため)

難読化、大事すぎ

参考


この記事が気に入ったらサポートをしてみませんか?