はじめに
Electronもいつの間にかv1になりましたね。v0の頃におぉ!ってなってちょっと触って終わってた程度なんですが、最近改めて触ってv0の時と使い方が色々違ってたので、公式ドキュメントを見ながら今から始める際に意識した方が良さそうなことをメモ。
開発モジュールの名前
今までelectron-prebuilt
という名前でインストールしていた開発モジュールですが、いつの間にかelectron
でインストールしてくれとなっています。electron-prebuilt
は2016年末までは公開してるとのことなので、今のうちに移動しましょう。
npm i -g electron
でグローバルインストールでもいいですが、ローカルに全て納めたい場合はnpm i -D electron
でpackage.jsonに記載してnpm scriptsかなんかでCLIを叩きましょう。
Electronの構成
この辺は昔と変わりません。
- package.json : Electronの起動に必要な情報
- MainProcess : Electronのアプリ(Node)自体の処理
- RendererProcess : アプリの中で動いているブラウザ(Chromium)の処理
なので、最小構成を目指すと下記のようになります。
app // Electronを起動するルート
├── package.json // Node特有のパッケージ管理、MainProcessの場所も指定
├── main.js // MainProcessのNode処理
└── renderer.html // RendererProcessで表示するHTML
この状態でappフォルダに対してElectronモジュールをコマンドで起動するとElectronが立ち上がります。
$ electron app
リリースする際はパッケージ化を行うのですが、その際にappフォルダの中身をそのままパッケージ化するケースが多いと思いますので、Electronに必要なものだけ配置するようにプロジェクト構成を考えましょう。
Electronはnpmの資産を十分に発揮して自由に作ることができますが、その分package.jsonにどんどん依存モジュールを書いてしまい生成されたnode_modulesの中がすごい容量になったりします。開発にしか使わないようなモジュールも入れた状態でアプリとしてリリースしたりすると、規模の小さいアプリの割に容量がすごい・・・みたいなことになってしまいます。
そこで、プロジェクトとして使うpackage.jsonとElectronとして使うpackage.jsonを分けて作成して開発したりすることでnode_modulesの分散化を図ったりすることが考えられたりします。
project
├── app
│ ├── Electronの実行に必要なファイル群
│ └── package.json
├── プロジェクトに必要なファイル群
└── package.json
こうすることで、appの中身で開発して、リリース時にプロジェクトの方のpackageでパッケージ化といったフローがやりやすいと思います。
Electronでのes2015対応状況
2016/09の時点で公式サイトだとこうなっているようです。
- Electron: 1.4.1
- Node: 6.5.0
- Chromium: 53.0.2785.113
- V8: 5.3.332.45
ここまで最新版が使えている状態だとimport/export構文が動かないくらいでes2015の記法は99%くらい使えるようです。なので、Babelなどでes2015を変換して〜といった設定なんかも必要無さそうという印象です。
Node自体は昔からCommonJSでimport/export構文のように取ってくることは可能なので、import/export構文絶対主義とかじゃなければrequire構文でモジュールを取得するように書いていくのが望ましいのかなと思います。Electron自体がNodeですしNodeの仕様に図りましょう。
// es2015
import moduleName1 from 'moduleName1';
import {objectName1, objectName2} from 'moduleName2';
// CommonJS
const moduleName1 = require('moduleName1');
const {objectName1, objectName2} = require('moduleName2');
// es2015
export default moduleName1;
export objectName1;
export objectName2;
// CommonJS
module.exports = moduleName1;
exports.objectName1 = objectName1;
exports.objectName2 = objectName2;
MainProcessのNode側は変換がなくても手軽な開発ができることがわかりましたが、RendererProcessのブラウザ側には本来requireオブジェクトが無いからどうすべきか迷うと思います。
そのことを考え、ElectronではこのあたりのNodeの仕様をwindowオブジェクトに拡張しており、window.requireという形でrequireオブジェクトがグローバルに存在しています。グローバルなので普通にrequire構文が書け、ブラウザ側でもCommonJSがデフォルトで動くようになっておりNodeと同じ感覚で使用することができます。
なので、BrowserifyやWebpackといったビルドツールが無くてもes2015でElectronを依存関係解決しながら開発できるといった状態です。
もちろん「リリース用に結合したり圧縮したい」「パフォーマンスが〜」といった場合などで使う場合もあると思いますので、開発中は別になくても済むくらい今では対応が進んでいるよと認識するくらいにしましょう。
ちなみにBrowserifyやWebpackで変換する場合、RendererProcessのブラウザ側でrequire('electron')
を含めたくないという問題が発生したりしますが、これはwindow.require('electron')
と記述することでBrowserifyやWebpackから誤認させて変換対象に含ませない、といったことで対処できたりします。
Electron APIの呼び出し方
const app = require('app');
const BrowserWindow = require('browser-window');
const ipc = require('ipc');
古い記事などでは上記の書き方をよく見るのですが、今では全てElectronからオブジェクトを引っ張ってくるように変更されています。視覚的にもElectronのAPIだと分かりやすくなったと思います。
// MainProcess
const {app, BrowserWindow, ipcMain} = require('electron');
// RenderProcess
const {ipcRenderer} = require('electron');
MainProcessとRenderProcess間の通信に使うipcなどは昔とは違い別々にipcMain、ipcRendererといった感じでモジュールが分離されるようになっています。逆に両方同じモジュールというのもあるので、公式ドキュメントのAPI Reference部分で使えるものをある程度見ておくことが望ましいと思います。
最後に
v1になってから、開発者向けにコーディングスタイルなんかも解説しており、徐々に整ってきてる感じがします。
また、既存のElectronプロジェクトなんかも知らないうちにだいぶ増えました。GitHubで公開されてるものなどは構成を覗いてみて自身の開発環境の参考にすると良いかと思います。