Electron入門➁:Preloadスクリプトの基本

この記事では、Electronにおける重要な機能の一つであるPreloadスクリプトについて、その概要と基本的な使い方をまとめています。

この記事で分かること
  • Electronにおける、Prelaodスクリプトの概要と用途
  • 基本的なPreloadスクリプトの実装方法と動作チェック
目次

Preload の概要

 Preload スクリプト は、一言で言うなら、Rendererプロセスで(安全に)Node.js機能を使用するための機能です。

通常、Node.jsの実行はメインプロセスのみに制限されており、レンダラープロセスでは実行することができません。
これに対し、Preload スクリプトはレンダラープロセスが読み込まれる前に(制限の適用前段階で)実行されるため
Node.js API にアクセスできるタイミングで、安全なインターフェース(bridge)を構築します。

【補足】なぜ Preload スクリプトで Node.js API にアクセスできるのか?

Electron の Preload スクリプトが Node.js API にアクセスできるタイミングで安全なインターフェースを構築できる理由は、Electron の プロセスとセキュリティモデルの設計にあります。

1. Preload は レンダラーがロードされる前に実行される

Preload スクリプトは BrowserWindow でページが読み込まれるより前、つまり最初の HTML/JS が動く前に実行されます。

new BrowserWindow({
  webPreferences: {
    preload: path.join(__dirname, 'preload.js'),
    contextIsolation: true,
    nodeIntegration: false,
  }
})

このタイミングでは、Electron のレンダラープロセスはまだ「Node.js API アクセスを拒否する設定(nodeIntegration: false)」で動く準備が整っていません。
この処理フローのため、Preload だけは Node.js API にアクセスできるように特別に許可されているのです。

2. Preload スクリプトは 信頼されたコードとして扱われる

Preload スクリプトは開発者が書いたコードで、外部から読み込まれることは基本的にありません。

そのため、Electron 側は「Preload だけは Node.js を使っても良い」と安全とみなしています。

3. contextIsolation: true により、Preload の実行環境は分離されている

  • Preload は「分離された独自のJavaScriptコンテキスト」で実行されます。
  • レンダラーとは別世界なので、Node.js API を使っても、レンダラーからは直接見えません。

この仕組みによって、安全に contextBridge.exposeInMainWorld() を使って、あらかじめフィルターされた API だけをレンダラーに渡せるのです。

基本的な構成と動作フロー

実際の構成と動作フローをまめると以下のようになります。

main.js(メインプロセス)
 └── BrowserWindow の作成時に preload.js を指定
        ↓
preload.js(Preload スクリプト)
 └── contextBridge と ipcRenderer を使って、安全な API を定義
        ↓
index.html(レンダラープロセス)
 └── window.api などで preload.js 経由の API を利用

主な用途

Preloadの主な用途は以下の通りです。

用途説明
🔒 セキュアな通信contextBridge を用いて、Node.js の全機能を渡さずに必要な部分だけレンダラーに公開できる。
📡 IPC通信の仲介ipcRenderer を通じて、メインプロセスとのやり取りを可能にする
(例:ファイル読み書き、OS操作)。
🛡️ Node.js 機能の制限レンダラーから直接 fsoschild_process などにアクセスさせないことで、セキュリティリスクを減らす。
📁 環境変数や設定値の注入起動時に Electron のバージョンやアプリの設定などを注入できる。
🔧 カスタムAPIの構築フロント側に使わせたい関数だけを window.api のような名前で渡すことが可能。

簡単な Preload スクリプトの実装

以下では、実際の例とともにPreloadスクリプトを実装するときの流れをまとめていきます。

※ここでの内容は、以下記事の内容をベースとしてすすめています。環境構築や設定については以下をご参照ください。

preloadスクリプト本体の作成

まずはElectronプロジェクトフォルダ直下に、preload.jsを作成し、以下のようなコードを記述します。

// Electron から必要なモジュールを読み込み
const { contextBridge, ipcRenderer } = require('electron');

// contextBridge を使って、window.api という名前でレンダラ側にAPIを公開
contextBridge.exposeInMainWorld('api', {

  // メインプロセスへメッセージを送信する関数
  sendMessage: (msg) => ipcRenderer.send('message', msg),

  // メインプロセスからの返信メッセージを受け取るためのリスナーを登録
  onReply: (callback) => ipcRenderer.on('reply', (_, data) => callback(data)),
});

メインプロセスでPreloadを読み込み

メインプロセス(main.js)にて、作成したPreloadスクリプトを使用するための設定を追加します。

// Electron の主要モジュールを読み込み
const {
    app,           // アプリケーションのライフサイクル(起動・終了など)を制御
    BrowserWindow, // ブラウザウィンドウ(レンダラー)の作成
    ipcMain        // メインプロセス側で、レンダラーとの通信を受け取る
} = require('electron');

const path = require('path'); // 絶対パス指定のため追加


console.log('main.js - start:OK')   // 起動確認テスト用出力

// ウィンドウ作成関数
const createWindow = () => {
    // 新規ウィンドウインスタンスを生成
    const win = new BrowserWindow({
        width: 800,
        height: 600,

        webPreferences: {
            // Preload スクリプトを指定
            preload: path.join(__dirname, 'preload.js'),

            // IPCセキュリティ設定
            contextIsolation: true,   // Preload とレンダラーの分離
            nodeIntegration: false,   // レンダラーで Node.js を制限
        },
    });

    // 作成したウィンドウに `index.html` を読み込む(ローカルファイル)
    win.loadFile('index.html');
};

// アプリの起動準備完了時
app.whenReady().then(() => {
    // ウィンドウを生成
    createWindow();

    //macOS用:Dockから再クリック時などに呼ばれるイベント`activate`発生時
    app.on('activate', () => {
        //ウィンドウがすべて閉じられていた場合、新しいウィンドウを再作成
        if (BrowserWindow.getAllWindows().length === 0) createWindow();
    });
});

// すべてのウィンドウが閉じられた時
app.on('window-all-closed', () => {
    // macOSではない場合、アプリ終了
    if (process.platform !== 'darwin') app.quit();
});

// レンダラープロセスからで送られてくる "message" イベントを受信(Preload経由)
ipcMain.on('message', (event, arg) => {
  // レンダラーから送られた内容をコンソールに表示
  console.log('Renderer says:', arg);

  // メインプロセスからレンダラープロセスへ返信メッセージを送信
  // 'reply' チャンネルでメッセージを送る → preload.js で onReply() によって受信可能
  event.reply('reply', 'Hello from main process');
});

補足:webPreferences 設定について

  • nodeIntegration: false にして、Preload 経由でのみ Node.js を使わせる。
  • contextIsolation: true を有効にすることで、レンダラーが Preload 以外で Node.js API を触れないようにする
  • 上記二つはElectronのデフォルト値として設定されているため、コード記述は省略できます(例ではわかりやすさのために記述しています)

レンダラーでのAPI使用

レンダラー用のJavascriptrenderer.jsを作成し、以下のように構成します。
※ HTMLのCSP設定でインラインJavascriptは制限しているため、外部ファイルに分離して構成してください

// DOMが読み込まれたらイベント設定
window.addEventListener('DOMContentLoaded', () => {
    const sendBtn = document.getElementById('sendBtn');
    const responseP = document.getElementById('response');

    // ボタンがクリックされたら、メッセージをメインプロセスに送信
    sendBtn.addEventListener('click', () => {
    window.api.sendMessage('こんにちは!Rendererからのメッセージです');
    });

    // メインプロセスからの返信を受け取って表示
    window.api.onReply((data) => {
    responseP.textContent = 'メインプロセスからの返信: ' + data;
    });
});

作成したrenderer.jsは、HTMLで以下のように使用します。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8" />

    <!-- コンテンツセキュリティポリシー(CSP)設定:XSSなどを防ぐための読み込み制限-->
    <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>Electronテストページ</title>
</head>

<body>
    <h1>Hello, Electron!</h1>
    <p>これはRendererプロセスで表示されています。</p>

    <!-- メッセージ送信ボタン -->
    <button id="sendBtn">メッセージ送信</button>
    <p id="response">メインプロセスからの返信はここに表示されます</p>

    <!-- レンダラーJSファイルを読み込み -->
    <script src="renderer.js"></script>
</body>

</html>

各通信の関係性

ここまでのコードでも扱った、各プロセス間通信のコードとその関係性は以下の通りになります。

処理概要
ipcRenderer.send('message', msg)レンダラー → メインに送信
ipcMain.on('message', callback)メインで受信し、処理
event.reply('reply', msg)メイン → レンダラーへ返信
ipcRenderer.on('reply', callback)レンダラー側で返信を受信

動作確認

package.json の登録したコマンド start (“start”: “electron .”) でElectronアプリを起動できます。

npm run start

起動したアプリのボタンをクリックすることで、アプリの表示ページとコンソールそれぞれに送信メッセージが表示されます。

【補足】コンソールで日本語表示が文字化けする場合

日本語が コンソールに表示されたときに文字化けする問題は、通常 コンソールの出力エンコーディングによるものです。

Windows の標準コンソール(cmd.exe や PowerShell)は、デフォルトで Shift_JIS(CP932) を使用しますが、Electron(Node.js)は UTF-8 を使ってログ出力します。
そのため、UTF-8 の日本語が Shift_JIS 環境で正しく表示できず、文字化けが起きます。

対処として、以下コマンドでUTF-8(65001)を使うようにコンソールの設定を行うことで正しい表示を確認できます。
※Visual Studio Code のターミナルでも同様に有効です

chcp 65001

元に戻すときは、以下コマンドを実行します。

chcp 932

また、現在の設定状態を確認することもできます。

chcp
現在のコード ページ: 65001

おすすめの本

おすすめの本:

  • URLをコピーしました!
目次