Electron小技集


こんにちわ。

Electronアプリを作る際、Google先生に聞いても中々教えてくれなかったけど少し考えたら簡単だった...
という初歩的だけど痒い所に手が届かなかったコード例を、備忘録も兼ねていくつかご紹介します。

ウィンドウ多重展開防止

Renderer
myButton.onclick = ()=> ipcRenderer.send("window-open");
Main
ipcMain.on("window-open", ()=>{
    myWindow = new electron.BrowserWindow({
        width: 1920,
        height: 1080
    });
    myWindow.loadURL("Window2.html");
});

新しくウィンドウを展開する際は、多分こんな感じのコードになると思います。
しかしこれだと、ボタンが押されてwindow-openメッセージが来る度に新規ウィンドウを開いてしまいます。
そこで。

Main
// 新規ウィンドウのインスタンス用変数をグローバルに作っておく
let myWindow;

ipcMain.on("window-open", ()=>{
    // インスタンスが作成されていなかったら作成する
    if(!myWindow){
        myWindow = new electron.BrowserWindow({
            width: 192,
            height: 108
        });
        myWindow.loadURL("Window2.html");

        // ウィンドウを閉じたらインスタンス破棄
        myWindow.once("close", ()=>{
            myWindow = null;
        });
    }
    // インスタンスが作成されていたら、そのウィンドウを最前面に持ってくる
    else{
        myWindow.focus();
    }
});

インラインコメントで粗方説明してしまいましたが、このようにすれば、ウィンドウの多重展開を防ぐ事が出来ます。

イベントリスナ乱立対策

Renderer
myButton.onclick = ()=>{
    // "window-open"に送信したら"window-status"で応答が返る
    ipcRenderer.send("window-open");
    ipcRenderer.on("window-status", (event, status)=>{
        // hogehoge
    });
};

ボタンが押されたらwindow-statusが登録されるコードですが、この例だと押された分だけ同じ物が多重登録されてしまい、そのうちメモリリークを起こしてしまいます。

イベントリスナは、登録する期間と発動回数を意識するようにし、適宜破棄する事が重要です。
自分で開けたドアは自分で閉めましょう、と言う事です。

具体的には2つ、解決方法があります。

.once()

.once()とは、1度だけ発動した後、自動的に破棄されるイベントリスナの事です。
.on("event");は、殆どの場合.once("event");に置換出来ます。

Renderer
myButton.onclick = ()=>{
    // "window-open"に送信したら"window-status"で応答が返る
    ipcRenderer.send("window-open");
    ipcRenderer.once("window-status", (event, status)=>{
        // hogehoge
    });
};

このようにすれば、ボタンが何回押されても1回発動したら破棄されるので、メモリリークは起きません。
(window-statusメッセージが来る前に高速連打したら話は別ですが...)

他にも分かりやすい活用例は、最初のウィンドウを起動させる際に良く使うapp.on("ready")です。
これは起動時しか使いませんよね。
ですがイベントリスナとしては、アプリケーションが終了するまで残存し続けます。
そこでapp.once("ready")に置き換える事で、アプリケーション起動時に発動したらすぐ破棄され、イベントリスナ数の節約になります。

.removeAllListeners()

.removeAllListeners()とは、登録済のイベントリスナを破棄するメソッドです。

2回以上発動するイベントで、多重登録や残存を避けたい場合に使います。

Main
// 文字列を受け取る
ipcMain.on("input-text-check", (event, text)=>{
    // 正しかったら"input-text-check"を破棄しウィンドウを表示
    if(text == "hoge"){
        ipcMain.removeAllListeners("input-text-check");

        myWindow = new electron.BrowserWindow({
            width: 1920,
            height: 1080
        });
        myWindow.loadURL("Window2.html");
    }
    // 違っていたら再入力待機
    else{
        event.sender.send("input-text-status", false);
    }
});

input-text-checkは、入力が1発で正しかったら1回しか発動せず、以後も必要ありませんが、間違っていたら再びリッスンしなければなりません。

このように、発動回数が不確定で条件を満たしたら必要無くなる、と言ったケースに有効です。

おわりに

毎度の事ですが、もっと良いやり方があったらゆるぼ、と言う事でよろしくお願いします。
おわり。