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

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

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

## イメージ図
こんな感じです。解説は[後述](#イメージ図解説)してます。
![イメージ図](https://qiita-image-store.s3.amazonaws.com/0/7442/345613d4-04b0-4699-c0bf-7a944cdb5083.png)


## Electronとは
webの技術（HTML,CSS,JS）でデスクトップアプリを作れるもの。
しかもクロスプラットフォーム対応。
[Atom](https://atom.io/)や[Slack](https://slack.com/)や[Visual Studio Code](https://code.visualstudio.com/)を作ってるやつ。
詳しくは->[Electron](http://electron.atom.io/)

## BrowserProcess と RendererProcess
Electronでは **BrowserProcess（Main Process）** と **RendererProcess** の２つのプロセスが走っています。

超噛み砕くと、
**BrowserProcess** は、GUIパーツの処理を行います。（[公式](http://electron.atom.io/docs/v0.37.8/tutorial/quick-start/#main-process)）
**RendererProcess** は、ブラウザの中のJSを処理します。（[公式](http://electron.atom.io/docs/v0.37.8/tutorial/quick-start/#renderer-process)）

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

それぞれのプロセスは別物なので基本的に通信はできません。
しかし、それらのプロセス間通信をするモジュールが提供されています。
それが、[remoteモジュール](http://electron.atom.io/docs/v0.37.8/api/remote/)になります。

## remote モジュール
BrowserProcessとRendererProcess間のプロセス間通信（IPC）の仕組みを提供するモジュールです。
RendererProcessからBrowserProcessのモジュールを使えるようにするモジュールです。（[公式](http://electron.atom.io/docs/v0.37.8/api/remote/)）

RendererProcess上で

```js
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モジュール経由でアクセスする**

## イメージ図解説
![イメージ図](https://qiita-image-store.s3.amazonaws.com/0/7442/345613d4-04b0-4699-c0bf-7a944cdb5083.png)

1. Electron実行で **BrowserProcess** 内で `package.json` のmainに書かれたファイル（ `main.js` ）が実行される
1. **BrowserProcess** から `mylib.js` を `require` 。 `mylib.js` の実体ができる。
1. `main.js` で `new BrowserWindow` することで、GUIのウィンドウが生成されて **RendererProcess** が動き出します。
1. ブラウザで `index.html` を読み込みます。
1. `index.html` から `index.js` が呼ばれます。
1. `remote` **経由** で `mylib.js` を呼び出すと、 `mylib.js` が **BrowserProcess** 下で動きます。
1. `remote` **なし** で `mylib.js` を呼び出すと、 `mylib.js` が **RendererProcess** 下で動きます。

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

## 例
### ソース

```json:package.json
{
  "name"    : "my-app",
  "version" : "0.1.0",
  "main"    : "main.js"
}
```

```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;
    });
});
```

```html: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>
```

```js: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;
};
```

```js:mylib.js
var mylib = {
    reg: function(val){
        this.value = val;
    },
    value: null,
};
module.exports = mylib;
```

### 結果
![](https://qiita-image-store.s3.amazonaws.com/0/7442/4c955869-93f1-7859-5045-5ae78314cac1.png)

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

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

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

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

## 参考リンク
- [Electron](http://electron.atom.io/)
- [Main Process](http://electron.atom.io/docs/v0.37.8/tutorial/quick-start/#main-process)
- [Renderer Process](http://electron.atom.io/docs/v0.37.8/tutorial/quick-start/#renderer-process)
- [remote](http://electron.atom.io/docs/v0.37.8/api/remote/)
- [Electronでアプリケーションを作ってみよう](http://qiita.com/Quramy/items/a4be32769366cfe55778)
- [Electronのremoteでプロセス間通信を高レベルに扱う](http://qiita.com/Misumi_Rize/items/a434cc0d17e5c6b85147)