初めてのアプリ作成
これは、Electron チュートリアルの**パート 2**です。
学習目標
このチュートリアルのパートでは、Electron プロジェクトの設定方法と、最小限のスターターアプリケーションを作成する方法を学習します。このセクションの最後までに、ターミナルから開発モードで動作する Electron アプリを実行できるようになります。
プロジェクトの設定
Windows マシンを使用している場合は、このチュートリアルに従う際に Windows Subsystem for Linux (WSL) を使用しないでください。アプリケーションを実行しようとすると問題が発生します。
npm プロジェクトの初期化
Electron アプリは、`npm init` を使用して `package.json` ファイルをエントリポイントとして npm でスキャフォールディングされます。フォルダーを作成し、その中に `npm init` を使用して npm パッケージを初期化することから始めます。
- npm
- Yarn
mkdir my-electron-app && cd my-electron-app
npm init
mkdir my-electron-app && cd my-electron-app
yarn init
このコマンドを実行すると、`package.json` のいくつかのフィールドを構成するように求められます。このチュートリアルの目的では、いくつかのルールに従う必要があります。
- エントリポイントは `main.js` である必要があります(このファイルはすぐに作成します)。
- 作成者、ライセンス、および説明には任意の値を使用できますが、後でパッケージ化するために必要になります。
次に、アプリの**devDependencies**に Electron をインストールします。これは、本番環境では必要ない、開発専用の外部パッケージ依存関係のリストです。
本番コードが Electron API を実行しているため、これは直感に反するように思えるかもしれません。ただし、パッケージ化されたアプリには Electron バイナリがバンドルされるため、本番環境の依存関係として指定する必要がなくなります。
- npm
- Yarn
npm install electron --save-dev
yarn add electron --dev
パッケージを初期化して Electron をインストールした後、`package.json` ファイルは次のようになります。また、Electron 実行可能ファイルを含む `node_modules` フォルダーと、インストールする正確な依存関係のバージョンを指定する `package-lock.json` ロックファイルも作成されます。
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "23.1.3"
}
}
Electron の直接インストールが失敗した場合は、ダウンロードミラー、プロキシ、およびトラブルシューティング手順に関する指示については、高度なインストールドキュメントを参照してください。
.gitignore の追加
.gitignore
ファイルは、Git で追跡しないファイルとディレクトリを指定します。プロジェクトのルートフォルダーにGitHub の Node.js gitignore テンプレートのコピーを配置して、プロジェクトの `node_modules` フォルダーをコミットしないようにする必要があります。
Electron アプリの実行
Electron の複数プロセスがどのように連携して動作するかをより深く理解するには、Electron のプロセスモデルドキュメントをお読みください。
`package.json`で定義したmain
スクリプトは、すべての Electron アプリケーションのエントリポイントです。このスクリプトは、**メインプロセス**を制御します。メインプロセスは Node.js 環境で実行され、アプリのライフサイクルの制御、ネイティブインターフェースの表示、特権操作の実行、およびレンダラープロセスの管理を担当します(詳細は後述)。
最初の Electron アプリを作成する前に、まず些細なスクリプトを使用して、メインプロセスのエントリポイントが正しく構成されていることを確認します。プロジェクトのルートフォルダーに、1行のコードを含む `main.js` ファイルを作成します。
console.log('Hello from Electron 👋')
Electron のメインプロセスは Node.js ランタイムであるため、`electron`コマンドを使用して任意の Node.js コードを実行できます(REPLとしても使用できます)。このスクリプトを実行するには、`package.json`のscripts
フィールドの`start`コマンドに`electron .`を追加します。このコマンドは、Electron 実行可能ファイルに現在のディレクトリにあるメインスクリプトを探して開発モードで実行するように指示します。
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"scripts": {
"start": "electron .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "23.1.3"
}
}
- npm
- Yarn
npm run start
yarn run start
ターミナルには `Hello from Electron 👋` と表示されます。おめでとうございます。Electron で最初のコードを実行しました!次に、HTML を使用してユーザーインターフェースを作成し、それをネイティブウィンドウに読み込む方法を学習します。
ウェブページを BrowserWindow に読み込む
Electron では、各ウィンドウは、ローカル HTML ファイルまたはリモートウェブアドレスから読み込むことができるウェブページを表示します。この例では、ローカルファイルを読み込みます。プロジェクトのルートフォルダーに、基本的なウェブページを含む `index.html` ファイルを作成することから始めます。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
</body>
</html>
これでウェブページができましたので、Electron BrowserWindow に読み込むことができます。`main.js` ファイルの内容を次のコードに置き換えます。各強調表示されたブロックについては、個別に説明します。
const { app, BrowserWindow } = require('electron')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
})
モジュールのインポート
const { app, BrowserWindow } = require('electron')
最初の行では、CommonJS モジュール構文を使用して 2 つの Electron モジュールをインポートしています。
- app は、アプリケーションのイベントライフサイクルを制御します。
- BrowserWindow は、アプリウィンドウを作成および管理します。
モジュールのキャピタライゼーション規則
**a**pp と **B**rowser**W**indow モジュールのキャピタライゼーションの違いに気付いたかもしれません。Electron はここで一般的な JavaScript の規則に従っており、PascalCase モジュールはインスタンス化可能なクラスコンストラクター(例:BrowserWindow、Tray、Notification)であるのに対し、camelCase モジュールはインスタンス化できません(例:app、ipcRenderer、webContents)。
型付きインポートエイリアス
TypeScript コードを記述する際の型チェックを向上させるために、`electron/main` からメインプロセスモジュールをインポートすることを選択できます。
const { app, BrowserWindow } = require('electron/main')
詳細については、プロセスモデルのドキュメントを参照してください。
Electron 28 以降では、ECMAScript モジュール(つまり、`import` を使用してモジュールを読み込む)がサポートされています。Electron での ESM の状態と、アプリでそれらを使用する方法の詳細については、ESM ガイドを参照してください。
ウィンドウをインスタンス化する再利用可能な関数の記述
`createWindow()`関数は、ウェブページを新しい BrowserWindow インスタンスに読み込みます。
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
アプリの準備ができたときに関数を呼び出す
app.whenReady().then(() => {
createWindow()
})
Electron のコアモジュールの多くは、Node の非同期イベント駆動アーキテクチャに従う Node.js イベントエミッターです。app モジュールはこれらのエミッターの 1 つです。
Electron では、app モジュールのready
イベントが発生した後にのみ、BrowserWindow を作成できます。app.whenReady()
API を使用してこのイベントを待ち、プロミスが実行されたら `createWindow()` を呼び出すことができます。
通常、エミッターの `.on` 関数を使用して Node.js イベントをリッスンします。
+ app.on('ready', () => {
- app.whenReady().then(() => {
createWindow()
})
しかし、Electron は ready
イベントに対して直接リスンすることによる微妙な落とし穴を避けるために、app.whenReady()
をヘルパーとして公開しています。詳細は electron/electron#21972 を参照してください。
この時点で、Electron アプリケーションの start
コマンドを実行すると、ウェブページを表示するウィンドウが正常に開きます!
アプリがウィンドウに表示する各ウェブページは、レンダラープロセス(または簡単にレンダラー)と呼ばれる個別のプロセスで実行されます。レンダラープロセスは、コードをバンドルおよび縮小するための webpack の使用や、ユーザーインターフェースを構築するための React の使用など、通常のフロントエンドWeb開発で使用されるものと同じJavaScript APIとツールにアクセスできます。
アプリのウィンドウライフサイクルの管理
アプリケーションウィンドウは、オペレーティングシステムごとに動作が異なります。Electron は、これらの規則をデフォルトで強制するのではなく、希望する場合はアプリコードで実装する選択肢を提供します。アプリと BrowserWindow モジュールのイベントをリッスンすることで、基本的なウィンドウ規則を実装できます。
Nodeの process.platform
変数を確認することで、特定のプラットフォームで条件付きでコードを実行できます。Electron が実行できるプラットフォームは、win32
(Windows)、linux
(Linux)、darwin
(macOS)の3つだけです。
すべてのウィンドウが閉じられたときにアプリを終了する(Windows と Linux)
Windows と Linux では、すべてのウィンドウを閉じると、通常アプリケーション全体が終了します。このパターンを Electron アプリで実装するには、app モジュールの window-all-closed
イベントをリッスンし、ユーザーが macOS を使用していない場合は、app.quit()
を呼び出してアプリを終了します。
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
ウィンドウが開いていない場合にウィンドウを開く(macOS)
対照的に、macOS アプリは、ウィンドウが開いていなくても一般的に実行を続けます。ウィンドウがない状態でアプリをアクティブにすると、新しいウィンドウが開きます。
この機能を実装するには、app モジュールの activate
イベントをリッスンし、BrowserWindow が開いていない場合は既存の createWindow()
メソッドを呼び出します。
ready
イベントの前にウィンドウを作成することはできないため、アプリが初期化された後でのみ activate
イベントをリッスンする必要があります。これは、既存の whenReady()
コールバック内で activate
イベントをリッスンすることによって行います。
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
最終的なスターターコード
- main.js
- index.html
const { app, BrowserWindow } = require('electron/main')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
<p id="info"></p>
</body>
<script src="./renderer.js"></script>
</html>
オプション:VS Codeからのデバッグ
VS Code を使用してアプリケーションをデバッグする場合は、メインプロセスとレンダラープロセスの両方に VS Code をアタッチする必要があります。実行するためのサンプル構成を次に示します。プロジェクト内の新しい .vscode
フォルダーに `launch.json` 構成ファイルを作成します。
{
"version": "0.2.0",
"compounds": [
{
"name": "Main + renderer",
"configurations": ["Main", "Renderer"],
"stopAll": true
}
],
"configurations": [
{
"name": "Renderer",
"port": 9222,
"request": "attach",
"type": "chrome",
"webRoot": "${workspaceFolder}"
},
{
"name": "Main",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"args": [".", "--remote-debugging-port=9222"],
"outputCapture": "std",
"console": "integratedTerminal"
}
]
}
サイドバーから「実行とデバッグ」を選択すると、「メイン + レンダラー」オプションが表示され、メインプロセスとレンダラープロセスの両方でブレークポイントを設定したり、すべての変数を検査したりすることができます。
launch.json
ファイルで行ったのは、3つの構成を作成することです。
Main
はメインプロセスを開始し、リモートデバッグのためにポート 9222 を公開する(--remote-debugging-port=9222
)ために使用されます。これは、Renderer
のデバッガーをアタッチするために使用するポートです。メインプロセスは Node.js プロセスであるため、型はnode
に設定されています。Renderer
はレンダラープロセスをデバッグするために使用されます。メインプロセスがプロセスを作成するため、新しいプロセスを作成する代わりに、それに「アタッチ」する必要があります("request": "attach"
)。レンダラープロセスはWebプロセスであるため、使用するデバッガーはchrome
です。Main + renderer
は、前のものを同時に実行する 複合タスク です。
Renderer
でプロセスにアタッチしているため、デバッガーが実行される前に接続するのに十分な時間がなかったため、コードの先頭行がスキップされる可能性があります。開発モードでコードを実行する前にページを更新するか、タイムアウトを設定することで、これを回避できます。
デバッグ領域をさらに深く掘り下げたい場合は、次のガイドで詳細情報を確認できます。
概要
Electron アプリケーションは npm パッケージを使用して設定されます。Electron 実行ファイルはプロジェクトの devDependencies
にインストールする必要があり、package.json ファイル内のスクリプトを使用して開発モードで実行できます。
実行ファイルは、package.json の main
プロパティにある JavaScript エントリポイントを実行します。このファイルは Electron のメインプロセスを制御し、Node.js のインスタンスを実行し、アプリのライフサイクル、ネイティブインターフェースの表示、特権操作の実行、およびレンダラープロセスの管理を担当します。
レンダラープロセス(または簡単にレンダラー)は、グラフィカルコンテンツの表示を担当します。WebアドレスまたはローカルHTMLファイルのいずれかを指定して、レンダラーにWebページをロードできます。レンダラーは通常のWebページと非常によく似ており、同じWeb APIにアクセスできます。
チュートリアルの次のセクションでは、特権APIを使用してレンダラープロセスを拡張する方法と、プロセス間で通信する方法について学習します。