[Electron] Webの技術だけでWindowsやmacOS用のアプリを作成する

HTMLやJavaScriptだけでPC用のアプリが作成できるツールはいくつかありますが、その中でもVisualStudioCodeやSlackなどの実績があるElectronは検討候補から外せないでしょう。

1つのソースコードで複数のプラットフォーム用のアプリを同時に開発できるのは非常に魅力的です。 今回はElectronでHelloWolrd的なアプリを作成し、実際にWindowsmacOSで動作する実行可能なファイルを作成するところまでをまとめてみます。

Electronのチュートリアルを試す

環境の確認

Node.jsが入っていない場合は公式サイトからダウンロード、インストールします。LTS版でも最新版でもどちらでも動きます。 https://nodejs.org/ja/nodejs.org

手元の環境は以下のようになっていたのでこのまま行きます。

$ node --version    
v12.16.2

$ npm --version 
6.14.8

初期設定

Node.jsのプロジェクトを始めるときと同様に、適当なディレクトリを作成し移動、npm initします。今回はチュートリアルを実施するだけなので設問項目はすべてエンターキー連打で構いません。

$ mkdir 1stapp; cd 1stapp 
$ npm init

npm initで作成されたpackage.jsonを開き7行目の内容を追加します。面倒な場合は省略しても構いません。

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

最後にElectronそのものをインストールします。ぼちぼち大きなファイルが落ちてきますので時間がかかります(インストール後のnode_modulesフォルダは190Mほどでした)

$ npm install --save-dev electron

ソースコード

今回はElectronの公式サイトにある内容をほぼそのまま利用します。

index.js

アプリの「側」になります。ウィンドウを開いたり、ウィンドウを閉じた際の挙動を制御します。

const { app, BrowserWindow } = require('electron')

/**
 * ウィンドウを作成する
 */
function createWindow () {
  // ウィンドウを新たに開く
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  // ファイルを開く
  win.loadFile('index.html')
}

// 初期化が終了したらウィンドウを新規に作成する
app.whenReady().then(createWindow)


// すべてのウィンドウが閉じられたときの処理
app.on('window-all-closed', () => {
  // macOS以外はアプリを終了する
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// アプリがアクティブになった時の処理
// (macOSはDocのアイコンがクリックされたとき)
app.on('activate', () => {
  // ウィンドウがすべて閉じられている場合は新しく開く
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
})

index.html

ウィンドウ内に表示する内容です。こちらは公式ドキュメントそのまま。通常のWebブラウザ用のHTMLと変わりませんが、JavaScript内でprocessというグローバル変数(オブジェクト)が利用されているのがわかりますね。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello World!</title>
  <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
</head>
<body>
  <h1>Hello World!</h1>
  We are using node <script>document.write(process.versions.node)</script>,
  Chrome <script>document.write(process.versions.chrome)</script>,
  and Electron <script>document.write(process.versions.electron)</script>.
</body>
</html>

metaタグのContent-Security-Policyはその名の通りセキュリティ関連の設定を行うものです。ここではJavaScriptが実行可能な場所を同じオリジン(アプリ内のJSファイル)、HTML内のscriptタグなどでの実行を許可しつつ、onclick=""などタグの属性での実行は禁止しています。

実行する

開発用に現在の状態を確認するために実行をしてみます。

$ npm start

以下のようにウィンドウが立ち上がり、index.htmlの内容が表示されました。

Windowsでも同様の結果が確認できます。

実行可能なファイルを作成する - electron-packager編

npm startではデバッグ用にパパっと表示することができますが、このままの状態では配布することができません。そこでElectron Packagerを利用し、WindowsmacOSで起動することができるアプリを生成します。

※詳しくは後述しますが、2020年現在では後継のelectron-builderの方が機能も充実しており支持を集めているようです。特にpackagerにこだわりがない場合は次のelectron-builderの章をご覧いただくのが良いかと思います。

インストール

先ほどの1stappフォルダで以下のコマンドを実行します。

$ npm install --save-dev electron-packager

生成する

以下を実行すると「sample-darwin-x64」フォルダが新たに作成され、その中にmacOS用の実行可能なファイルが生成されます。

$ npx electron-packager . sample --platform=darwin --arch=x64

WindowsLinux用のアプリを生成する場合には --platformで指定している箇所を以下のように変更するだけです。

オプション 説明
win32 Windows用のEXEファイルを生成
linux Linux用のファイルを生成
darwin macOS用のappファイルを生成
mas macOS用のAppStoreで販売するためのファイルを生成

また--archには以下のようなアーキテクチャを指定します。そのうちmacOSはARMを指定することになりそうですね。

  • ia32
  • x64
  • armv7l
  • arm64
  • mips64el

ちなみに、例えばmacOSWindows用のファイルを作成することも可能なようですが、動作確認の問題もあるので基本的にはWindows用のファイルを生成する場合はWindowsで、macOSの場合はmacOS上で実行するのが良さそうですね。

npm scriptsにまとめる

毎回このコマンドを打つのは面倒なので、npm scriptsにまとめておきます。

{
  "name": "1stapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "electron .",
    "build-mac": "electron-packager . sample --platform=darwin --arch=x64  --overwrite",
    "build-win": "electron-packager . sample --platform=win32 --arch=x64  --overwrite",
    "build-linux": "electron-packager . sample --platform=linux --arch=x64  --overwrite"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "electron": "^10.1.2",
    "electron-packager": "^15.1.0"
  }
}

すると以下のように簡潔なコマンドで実行可能になります。

$ npm run build-mac

実行可能なファイルを作成する - electron-builder編

electron-builderはpackagerよりも高機能なツールです。 例えば以下のような機能が利用できます。

  • 自動更新
  • 様々な出力形式
  • ビルド後のファイルをGitHub Releases, Amazon S3, DigitalOcean Spacesへ公開
  • 好きなプラットフォームでLinuxまたはWindows用のElectronアプリをビルドするためのDockerイメージ。

とりあえずインストーラーを自動で付けてくれるのはありがたい。

インストール

先ほどのelectron-packagerをインストールしている場合はいったん抜きます。またpackage.jsonscriptsに追加した内容も消しておきます。

$ npm remove electron-packager

その後electron-builder先生をインストール。

$ npm install --save-dev electron-builder

package.jsonで設定する

electron-builderではpackage.jsonにビルド設定を記述します。scriptsにはそれぞれmacOSWindows用のビルドコマンドのショートカットをまとめ、build内に具体的な設定をまとめています。

{
  "name": "1stapp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
      "start": "electron .",
      "build-mac": "electron-builder --mac --x64",
      "build-win": "electron-builder --win --x64"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "electron": "^10.1.2",
    "electron-builder": "^22.8.1"
  },
  "build":{
    "appId": "net.makitokatsube.app.1st",
    "files": [
      "package.json",
      "package-lock.json",
      "index.js",
      "index.html"
    ],
    "mac":{
      "target": "dmg"
    },
    "win":{
      "target": "nsis"
    },
    "nsis":{
      "oneClick": false,
      "allowToChangeInstallationDirectory": true
    }
  }
}
appId
OSがアプリを識別する際に、他のアプリと重複しない文字列。いわゆるBundleIDです。自分で所有しているドメインを逆にしたものを指定しています。
files
アプリ内に埋め込むファイル。これを指定しないとnode_modulesフォルダなども入りファイル容量が大変なことになります。ここではファイルだけではなくフォルダも指定できるのでファイル数が多くなってきたらフォルダにまとめてやります。
mac, win
それぞれOSごとのビルド設定を記述します。targetは最終的な出力形式の指定です。
nsis
Windowstargetにnsisを指定した場合ここで具体的な設定を行います。よくミカエルボタンをどんどん押していく形式のインストーラとして動いてくれます。なおoneClickはクリック一発でインストールを完了するか、allowToChangeInstallationDirectoryはインストール先の指定を許可するかどうかの設定になります。

これ以外にも様々なオプションが用意されているので、必要に応じて指定します。 www.electron.build

生成する

では実際に各OSごとに実行可能なファイルを生成します。

macOS用アプリ

package.jsonのscriptsに登録したコマンドを実行します。

$ npm run build-mac

すると以下のようにdistフォルダが作成され、dmgファイルが生成されているのがわかります。dist/macフォルダの下にあるappファイルがインストーラーに入る前の純粋なアプリです。こちらもそのまま利用できます。

dmgファイルを開くと、アプリのインストール時によく見かける「アレ」が眼前に!テンション上がりますねw

実際にApplicationフォルダへコピーするとLaunchPadにアイコンが出現します。ついでにDocに追加することももちろん可能です。

Windows用アプリ

こちらもpackage.jsonのscriptsに登録したコマンドを実行します。

$ npm run build-win

するとこちらもdistフォルダの下に、Windows用のインストーラーであるEXEファイルが作成されます。dist\win-unpackedフォルダの下にはインストーラーに含まれる実際のアプリケーションを確認できます(ここからも起動できます)。

EXEを実行するとこれもよく見かけるインストーラーが起動します。「次へ」ボタンを連打する感じ、まさにアレですね!

インストールが完了すると「スタート」メニューから起動できます。またアンインストールもWindowsの「設定」→「アプリ」から行うことができます。OSから正式なアプリと認めてもらえてるようで最初は興奮しますねーw

今回のソースコード

GitHubに上げました。こちらからもご覧いただけます。 github.com

まとめ

セットアップからHelloWorldアプリを作成するまで10分もあれば出来てしまうのは感動しますね。しかもクロスプラットフォーム!中身はChromiumがそのまま入っているようなので、最近だとReactやVueなど好きなフレームワークを利用できるのも良いですね。

反面、Chromiumがそのまま中に入ることから、今回のようなHelloWorldアプリのみでも生成されるアプリ自体の容量がmacOS版だと168Mbyteとかなり大きくなってしまいます。小規模なアプリを開発する場合には少しためらいますね。また基本的にはC/C++などでネイティブアプリを作成するのと比べるとパフォーマンスは出ないのはトレードオフですね。

すでにあるWebサービスをローカルで動かしたい、アプリとして配布したいといった場合など利用シーンがマッチすればかなり便利に使えそうです。

参考ページ