Chapter 04

Electron の仕組みを理解する

Kei Touge
Kei Touge
2021.10.02に更新

ファイル・フォルダ構成を確認する

では、このアプリのソースコードを見てみましょう。
このプロジェクトフォルダ myapp の構造は以下のようになっていると思います。

bash
% tree -a -L 1
.
├── app.js
├── index.html
├── main.js
├── node_modules
├── package-lock.json
└── package.json

1 directory, 5 files

main.js を見てみる

このアプリを起動するときに Electron が最初に読み込むスクリプトが main.js でした。では、この main.js の内容を確認しましょう。

main.js
const { app, BrowserWindow } = require('electron');

const createWindow = () => {
  const mainWindow = new BrowserWindow({
    width: 600,
    height: 400,
    title: 'マイアプリ',
  });

  mainWindow.loadFile('index.html');
};

app.once('ready', () => {
  createWindow();
});

app.once('window-all-closed', () => app.quit());

冒頭行で require 文を使って、先ほどインストールした electron モジュールを読み込んでいます。
これは CommonJS と呼ばれる JavaScript の形式の一つで、ざっくり言うと Node.js で JavaScript を実行する場合に使用される形式です。

Node.js === CommonJS という訳ではありません
詳細は下記の Wikipedia 記事を参照してください。

http://ja.wikipedia.org/w/index.php?curid=3439896

http://ja.wikipedia.org/w/index.php?curid=2217415

第 1 章で少しだけ触れたように、Electron アプリが OS の機能を利用する部分は Node.js が担当するとなっていましたね。なので OS 上のアプリケーションとしての役割、つまりアプリ自身の起動や終了、タイトルバーやメニューバーの利用などの部分は CommonJS を使って記述します。

Babel などのツールによるトランスパイルを前提とするのであれば ES modules 形式で記述することも可能ですが、この本ではトランスパイルについての解説は行いません。

この「Electron アプリが OS の機能を利用する部分」のことを メインプロセス と言います。

main.js をもっと詳しく見てみる

引き続き main.js を見ていきましょう。

1 行目: require() 文

main.js
const { app, BrowserWindow } = require('electron');

先ほどインストールした electron パッケージから app モジュールと BrowserWindow クラスをインポートしています。

app は、アプリ全体の起動・終了などのライフサイクルを制御するモジュールです。

BrowserWindow クラスではそのインスタンスを作成することで、アプリが利用するウィンドウの作成・制御が可能となります。

3 行目: createWindow() 関数

main.js
const createWindow = () => {
  const mainWindow = new BrowserWindow({
    width: 600,
    height: 400,
    title: 'マイアプリ',
  });

  mainWindow.loadFile('index.html');
};

mainWindow という名前で BrowerWindow のインスタンス(=ウィンドウ)を作成する関数です。

new BrowserWindow()オプションでは、ウィンドウのサイズやタイトルバーに表示する文字列などを指定することができます。

さらに、 この mainWindow インスタンスに index.html というファイルを読み込ませています(後述)。

13, 17 行目: app モジュール

app はアプリ全体のライフサイクルを制御するモジュールでしたね。そして app.on(once) はアプリのライフサイクルイベントを待ち受けるイベントリスナーです。

構文
app.on('イベント名', コールバック関数);

https://sbfl.net/blog/2019/02/08/javascript-callback-func/

13 行目では、アプリの起動イベントに対して createWindow() 関数を実行してウィンドウを作成し、そのウィンドウが index.html を読み込んでいます。

main.js
app.once('ready', () => {
  createWindow();
});

17 行目では、「すべてのウィンドウが閉じられた」というイベントに対応してアプリ自身を終了させています。

main.js
app.once('window-all-closed', () => app.quit());

レンダラープロセス

さて、アプリのウィンドウが作成され、そのウィンドウが HTML ファイル (= index.html)を読み込むと、その後ウィンドウ内に描画されるコンテンツはその HTML ファイル(とそこから読み込まれるスクリプト)が受け持つこととなります。

fig10

index.html を見てみましょう。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>マイアプリ</title>
  </head>
  <body>
    <h1>Hello, world.</h1>

    <script src="app.js"></script>
  </body>
</html>

ウィンドウ内に Hello, world. という文字列を表示し、app.js というスクリプトを読み込んでいます。そして、その app.js はデベロッパーコンソールへ Hello, world. と出力しています。

app.js
console.log('Hello, world.');

もう気づいているかもしれませんが、index.html が読み込まれた後は通常の Web サイトと変わりありません。

第 1 章でも少しだけ触れましたが、この HTML ファイルが読み込まれた後の処理は Chromium(=ウェブブラウザ)が担当するのでしたね。

この「HTML が読み込まれたのちの Chromium が担当する Web サイトと(ほぼ)同じ部分」を レンダラープロセス と言います。

そして、このレンダラープロセスを実行するのは Node.js ではないため、CommonJS 形式の記法は使えません。 必然的に ECMAScript 形式で記述していくこととなります。

次章では、この "メインプロセス""レンダラープロセス" の関係を見ていきましょう。