Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
70
Help us understand the problem. What is going on with this article?

More than 5 years have passed since last update.

@tkhr

【図解】ElectronのBrowserProcessとRendererProcessにハマったのでモジュールとの関係を図にした

はじめに

JSをほぼほぼ書いたことない私がElectronでアプリを作りながらJavaScriptを勉強すれば、JSに触れられて、HTML&CSSの理解を深められて、今HotなElectronもできて一石四鳥じゃんと思いアプリを作り始めました。
しかし、BrowserProcessとRendererProcessの関係(あとJS自体の仕様も関係ある?)で勘違いしてハマりにハマったので、理解したイメージを図にします。
ドキュメントにはちゃんと文章で書いてありますが、イメージとしてつかんだ方がわかりやすかったです。

動作確認したElectronのバージョンは 0.37.8 です。

イメージ図

こんな感じです。解説は後述してます。
イメージ図

Electronとは

webの技術(HTML,CSS,JS)でデスクトップアプリを作れるもの。
しかもクロスプラットフォーム対応。
AtomSlackVisual Studio Codeを作ってるやつ。
詳しくは->Electron

BrowserProcess と RendererProcess

Electronでは BrowserProcess(Main Process)RendererProcess の2つのプロセスが走っています。

超噛み砕くと、
BrowserProcess は、GUIパーツの処理を行います。(公式
RendererProcess は、ブラウザの中のJSを処理します。(公式

つまり、
BrowserProcessは重い処理もできますよ。
RendererProcessはDOM描画系の処理をさせる用ですよ。
といった感じです。

それぞれのプロセスは別物なので基本的に通信はできません。
しかし、それらのプロセス間通信をするモジュールが提供されています。
それが、remoteモジュールになります。

remote モジュール

BrowserProcessとRendererProcess間のプロセス間通信(IPC)の仕組みを提供するモジュールです。
RendererProcessからBrowserProcessのモジュールを使えるようにするモジュールです。(公式

RendererProcess上で

const remote = require('electron').remote;
const BrowserWindow = remote.BrowserWindow;

var win = new BrowserWindow({ width: 800, height: 600 });
win.loadURL('https://github.com');

こんな感じにすると、GUI操作ができてしまう。
ということです。

問題の背景

mylib.jsが状態を保持する、かつ、重い処理をするプログラムでした。
そこで、BrowerProcessに処理してもらおうと、 remote.require して処理してもらいました。
index.jsから remote.require した時のmylib.jsの状態と、 require したhoge.jsから require した時のmylib.jsの状態が違いました。

つまり、 重い処理の時はremote経由でrequireしておけばいいんでしょ。 という感じでした。
remoteを経由しようがしまいが、 同じ参照にアクセスできると思っていたことが間違い でした。

原因と解決策

原因

remoteを経由してモジュールをrequireすると、remoteなしでrequireした時とは別の参照にアクセスするっぽい。

解決策

remoteの使い所は考える。
remote使うとremote無しの場合と別ものを参照する。

重い処理の時はremote経由でrequireしておけばいいんでしょ
-> 重い処理はBrowserProcessで動かして、RendererProcessからremoteモジュール経由でアクセスする

イメージ図解説

イメージ図

  1. Electron実行で BrowserProcess 内で package.json のmainに書かれたファイル( main.js )が実行される
  2. BrowserProcess から mylib.jsrequiremylib.js の実体ができる。
  3. main.jsnew BrowserWindow することで、GUIのウィンドウが生成されて RendererProcess が動き出します。
  4. ブラウザで index.html を読み込みます。
  5. index.html から index.js が呼ばれます。
  6. remote 経由mylib.js を呼び出すと、 mylib.jsBrowserProcess 下で動きます。
  7. remote なしmylib.js を呼び出すと、 mylib.jsRendererProcess 下で動きます。

2と7の mylib.js の実体はそれぞれ別ものになります。
なので、valueの値も別ものになります。

ソース

package.json
{
  "name"    : "my-app",
  "version" : "0.1.0",
  "main"    : "main.js"
}
main.js
'use strict';

const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;

var mainWindow = null;

app.on('ready', function() {
    mainWindow = new BrowserWindow({width: 800, height: 600});
    mainWindow.loadURL('file://' + __dirname + '/index.html');
    mainWindow.on('closed', function() {
        mainWindow = null;
    });
});
index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Hello World!</title>
    </head>
    <body>
        <h1 id="h">Hello World!</h1>
        <p id="p1"></p>
        <p id="p2"></p>
        <p id="p3"></p>
        <p id="p4"></p>
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
index.js
'use strict';

var remote = require("remote");

// RenderereProcess
var mylib1 = require("./mylib");
var mylib2 = require("./mylib");
// BrowserProcess
var mylib1_r = remote.require("./mylib");
var mylib2_r = remote.require("./mylib");

window.onload = function(){
    mylib1.reg("hogehoge 1");
    mylib2.reg("hogehoge 2");
    mylib1_r.reg("foobar 1");
    mylib2_r.reg("foobar 2");

    document.getElementById("p1").innerHTML = mylib1.value;
    document.getElementById("p2").innerHTML = mylib2.value;
    document.getElementById("p3").innerHTML = mylib1_r.value;
    document.getElementById("p4").innerHTML = mylib2_r.value;
};
mylib.js
var mylib = {
    reg: function(val){
        this.value = val;
    },
    value: null,
};
module.exports = mylib;

結果

index.jsmylib1.value != mylib1_r.value となっています。
これは、それぞれがRendererProcess上とBrowserProcess上の参照で異なっているからです。

さらに、 index.js
mylib1.value == mylib2.valuemylib1_r.value == mylib2_r.value となっています。
RendererProcess上のとBrowserProcess上はそれぞれ同じ実体を参照しています。

まとめ

  • Electronには BrowserProcessRendererProcess がある。
  • BrowserProcessとRendererProcessは 別の空間 がある。
  • それぞれの通信には remoteモジュール を使う。

JSの参照の仕組みの理解がなかったことが事態をややこしくしていました。

参考リンク

70
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
70
Help us understand the problem. What is going on with this article?