スポンサーリンク

Electronでrequireができない?【プロセスとコンテクストとプリロードスクリプト】

ニッチな話題プログラミング

Electronでスクリプトを書いていたところ、Node.jsのAPIとDOM要素を結びつけるのに迷いました。

main.jsではDOM要素にアクセスできず、HTMLから読み込んだスクリプト(renderer.js)ではNode.jsのAPIにアクセスできないからです。

プロセスモデル | Electron」をもとに、それぞれの役割の違いを見てみましょう。

ポイント
  • main.jsは、メインプロセスを管理。
  • renderer.jsは、レンダラープロセスを管理。
  • preload.jsは、レンダラープロセスにNode.js APIを公開する。

以前バージョンでは、レンダラープロセスから直接Node.js APIが利用できましたが、セキュリティのために制限されるようになりました。

スポンサーリンク

3つのスクリプトと2つのプロセス

見様見真似でスクリプトを編集していると、思うように requireができなかったりして混乱しました。

Uncaught ReferenceError: require is not defined at renderer.js

このようなReferenceError(参照エラー)が発生するのは、スクリプトによって、動作するプロセス・コンテクストが異なります。

Electronのサンプルをみると、3種類のJavaScriptファイルがあります。

3つのJavaScript
  • main.js … package.jsonで指定するエントリ ポイント
  • preload.js … Window生成時にロードされる
  • renderer.js … index.htmlから読み込む
出典:ダークモード | Electron

メインプロセスとレンダラープロセス

Electronアプリは、メインプロセスとレンダラープロセスに分かれています。これは、Chromeブラウザの設計に由来します。

プロセスモデル | Electron

以前のブラウザでは、これらの機能を単一のプロセスで実現していました。 このやり方は(…)、1 つのウェブサイトがクラッシュしたりハングアップしたりすると、ブラウザ全体に影響が及びます。

この問題を解決するため、Chrome チームは各タブがそれぞれのプロセスで描画するようにすると決め、ウェブページ上のバグや悪意のあるコードがアプリ全体に与える影響を制限することにしました。 単一のブラウザプロセスはこれらのプロセスを制御し、アプリケーションのライフサイクル全体を制御します。 

Electron アプリ開発者の場合、メインとレンダラーの 2 種類のプロセスを制御します。 これらは、上述の Chrome 独自のブラウザプロセスとレンダラープロセスと似ています。

プロセスモデル | Electron

メインプロセスは、サーバサイド、
レンダラープロセスは、クライアントサイドのスクリプトです。

以前のElectronのバージョンでは、レンダラープロセスからもNode.js環境を生成できましたが、現在は無効になっています。「クロス・サイト・スクリプティング」の脆弱性になる恐れがあるからです。

注意: 開発を容易にするために、レンダラープロセスを完全な Node.js 環境で生成できます。 歴史的には、これがデフォルトでしたが、セキュリティ上の理由からこの機能は無効になりました。

プロセスモデル | Electron

このため、少し古い情報を頼りにコードを書くと、「なぜか動作しない」ということになります。

メインプロセス

Electron アプリのメインプロセスは一つです。

メインプロセスの特徴
  • アプリケーションのエントリポイント
  • Node.js 環境で動作

Node.js環境で動作するということは、
・モジュールを require したり
・Node.js のすべての API を利用したりできます。

メインプロセスの役割
  • ウィンドウを作成・管理する
  • アプリケーションを管理する(終了など)

メインプロセスのスクリプトは、package.jsonで指定します。

レンダラープロセス

レンダラープロセスは、メインプロセスがウインドウを作成したときに開始し、対応するBrowserWindow インスタンスが破棄されると終了します。

レンダラープロセスの特徴
  • レンダラープロセスのエントリーポイントはHTML ファイル
  • 実行する JavaScript コードは <script> 要素で追加する
  • UI のスタイルは CSS で追加する
  • レンダラープロセスは require やその他 Node.js の API に直接アクセスできない

レンダラープロセスのスクリプトは、HTML内の<script>要素で直接記述するか、読み込むかして動作します。

外部スクリプトを読み込む可能性があるため、require やその他 Node.js の API は制限されています。

BrowserWindow::webContents

メインプロセス側からウェブコンテンツを操作するには、ウインドウの webContents オブジェクトを使います。

プロセスモデル | Electron

プリロードスクリプト

プリロードスクリプトは、レンダラープロセス内でコンテンツ読込み前に実行されます。

プリロードスクリプトの主な役割は、レンダラースクリプトからNode.js API にアクセスできるように、contextBridge モジュールで公開することです。

プリロードスクリプトの特徴
  • レンダラープロセスのコンテクストで動作する
  • Node.js環境にアクセスできる

webPreference.preload

プリロードスクリプトは、メインプロセスでウィンドウを生成するときに、webPreferences オプションで関連付けられます。

プリロードスクリプトは、レンダラーのコンテキスト内で実行されます。しかし、HTMLではなくメインプロセスで指定されたスクリプトなので、特別に Node.js の API にアクセスできる権限が与えられています。

contextBridge.exposeInMainWorld

デフォルトでは、プリロードスクリプトの特権的 API は、レンダラーのメインワールドから分離されています。これは、「コンテキスト分離 (contextIsolation)」という仕組みで、外部ウェブコンテンツがAPIを悪用するのを防いでいます。

プリロードスクリプトで、contextBridge.exposeInMainWorldメソッドを使うと、windowを通して値や関数を公開できます。

プロセスモデル | Electron

例えば、レンダラーからでメインプロセスにプロセス間通信 (IPC)が必要なら、ipcRenderer ヘルパーをレンダラーに公開すればよいわけです。

ネイティブなファイルのドラッグ&ドロップ | Electron

こちらもどうぞ

QRコードを読み込むと、関連記事を確認できます。
Electronでrequireができない?【プロセスとコンテクストとプリロードスクリプト】
タイトルとURLをコピーしました