こんにちは、MSKです。
今更ながら、Electronに興味が出たので勉強中です。
その途中でレンダラープロセスからメインプロセスの機能を扱う部分が、動かず苦労しました。
原因と解決策を備忘録として残します。
レンダラープロセスでrequireを呼ぶとrequire is not defined
現在、掌田津耶乃氏の「Electronではじめるデスクトップアプリケーション開発」でElectronについて勉強をしています。
ちゃんと説明されていて、入門としては良い本だと思います。
もともと、僕は業務でC#を使ってWindowアプリケーションを作成していましたが、モダンなWeb技術を使った開発も取り入れてみたいと思って、今更ながらElectronに入門してみました。
この本の中にremoteを使って、レンダラープロセスからメインプロセスの機能を使うという話があるのですが、Electron v12.xを使っているのであれば、この本のコードをそのまま使ってしまうと動きません。
この本はv10.xをベースにしているので最新のものとは少し違う部分があるということには注意しないといけません。
この本では次のようにwebPreferencesのnodeIntegrationをtrueにして、呼び出すhtmlファイルのなかでrequireを呼んでいますが、これを行うとUncaught ReferenceError:require is not definedとエラーが出力されます。
(エラーを見るためには以下でコメントアウトしている行のコメントを外せばよいです。)
const {app,BrowserWindow} = require('electron'); function createWindow () { let win = new BrowserWindow({ width: 1000, height: 800, webPreferences: { nodeIntegration: true, enableRemoteModule: true, }, }); win.loadFile("index.html"); // win.webContents.openDevTools(); } app.whenReady().then(createWindow);
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sample App</title> </head> <body> <div> <button onclick="create()"> Click </button> </div> <script> const {remote} = require('electron'); const {BrowserWindow} = remote; function create(){ let win = new BrowserWindow({ width: 1000, height: 800, webPreferences: { nodeIntegration: true, enableRemoteModule: true, }, }); win.loadFile("index.html"); } </script> </body> </html>
問題の原因と解決方法
Electron v12で大きな変更があり、簡単に言うとレンダラープロセスからNode.jsの機能が呼び出せなくなりました。このあたりのドキュメント参照。
v12以降は次のように書くことになります。
メインプロセス側では次のようにpreload.jsを設定します。
const {app,BrowserWindow} = require('electron'); const path = require('path'); function createWindow () { let win = new BrowserWindow({ width: 1000, height: 800, webPreferences: { nodeIntegration: false, enableRemoteModule: true, contextIsolation: true, preload: path.join(__dirname, 'preload.js') }, }); win.loadFile("index.html"); } app.whenReady().then(createWindow);
preload.jsには次のようにcontextBridge.exposeInMainWorldにAPIを登録します。
メインプロセスとレンダラープロセスの間の橋渡しとなるコードを書きます。
const {remote,contextBridge} = require('electron'); const {BrowserWindow} = remote; const path = require('path'); const createNewWindow = () => { let win = new BrowserWindow({ width: 1000, height: 800, webPreferences: { nodeIntegration: false, enableRemoteModule: true, contextIsolation: true, preload: path.join(__dirname, 'preload.js') }, }); win.loadFile("index.html"); } contextBridge.exposeInMainWorld('testapi', { createNewWin: createNewWindow, } )
最後にレンダラープロセスでは登録したtestapiのcreateNewWinをwindow.testapi.createNewWinとして呼び出します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sample App</title> </head> <body> <div> <button onclick="window.testapi.createNewWin()"> Click </button> </div> </body> </html>
ボタンを押すと新しいウィンドウが作られます。
最後に
勉強初めてすぐに動かなくて、結構苦労しました。
そもそもC#とか組み込みとか、そんなに変更の速度が速くないので、面食らいました。
この本が出て半年くらいしかたっていないのにメジャーバージョンが2上がるとか・・・
苦労はしましたが、今のところElectron、なかなか面白いです。
作ってみたいアプリもいくつかあるので、さっさと基本マスターしてどんどん作っていこうと思っています。
また、新しい発見などがあったら記事にしたいと思います。
最後までご覧いただき、ありがとうございます。
「Electronのremoteで苦労した話!require is not definedが発生する・・・」でした。