LoginSignup
69
70

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-05-10

はじめに

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の参照の仕組みの理解がなかったことが事態をややこしくしていました。

参考リンク

69
70
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
69
70