React+Electronアプリを作ってみよう

origamium

Origami

Posted on March 5, 2018

React+Electronアプリを作ってみよう

こんにちは。前置きは抜きにすぐ作ります。

Create Application

01: create-react-app

まずはcreate-react-appです。なかったらnpm install -g create-react-appしてください。
うまくいったらyarn startで起動しましょう。しましたか?うまくいってるのを確認したら即刻ジョブ止めましょう。もう二度とyarn startを実行することはありません。

02: install Some Package

以下のパッケージをインストールしてください。最新ので問題はないはずです。すべてインストール時に--dev-dependenciesを付けることを忘れないでください。まあ忘れてもいいです。

  • electron
  • npm-run-all

03: setting up Electron

こちらのgistを参照してください。
次にelectron-starter.jselectron-wait-react.jsapplication.jssrc/以下に配置してください。色々書いてありますが後ほど説明します。

04: package.json

package.jsonにmainとhomepage、そしてscriptsに追記します。以下のようになればいいです。

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "main": "src/electron-starter.js",
  "homepage": "./",
  "dependencies": {
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "react-scripts": "1.1.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "electron:dev": "electron .",
    "dev": "npm-run-all --parallel electron:dev start"
  },
  "devDependencies": {
    "electron": "^1.8.2",
    "npm-run-all": "^4.1.3",
  }
}

Enter fullscreen mode Exit fullscreen mode

いくつか興味深いところはありますが、後ほど解説します。今はこれをただ受け入れてください。でも名前は好きに変えてもいいのよ。

05 start!

ではいよいよ動かす時が来ました。yarn devあるいはnpm run devを実行し、ブラウザとElectronが立ち上がって、同じ画面が映っていることを確かめてください。

また、もう既にこの時点でElectron側にもホットロードが機能していることも試してみましょう。src/App.jsの構文をわざと間違えて、ブラウザとElectronの両方の画面がエラーになることを確認するのです。

ホットロードまで確認したら、おめでとうございます!あなたはReactでElectronアプリケーションを作ることに成功しました。
さて、いくつか気になる所があったはずです。それらは次の項で説明します。

Explanation Create Application

Structure

今回のElectronアプリケーションの構造は比較的単純です。react-scriptsによって起動したWebサーバに、Electronがアクセスして表示しているだけです。本当にこれだけです!図にするまでもありませんが、図にしてみましょう。

ところで、react-scriptselectronnpm-run-allによってそれぞれ起動されます。このとき2つは同時に起動しています。

Security Warning?

まず、あなたが最新のElectronで開発している場合、yarn devあるいはnpm run devを実行した場合にconsoleに次のような警告が出ているはずです。

Electron Security Warning This renderer process has Node.js integration enabled and attempted to load remote content. This exposes users of this app to severe security risks.
For more information and help, consult https://electronjs.org/docs/tutorial/security
Enter fullscreen mode Exit fullscreen mode

これは要するに「なんかWebページをElectronで見てるけどここに悪意あるコード仕込まれてたらすごいやばいことが起きるぞ!」という警告です。
なぜ、この警告が出たかは自明ですね?今回の(開発時の)設定では、静的なHTMLやJavascriptファイルではない、localhostに立ち上がっているWebサーバ localhost:[port number] をElectronでアクセスしているために、このエラーが発生しています。
勿論ビルド時にはWebサーバーではなく、ビルドした静的なhtml, css, javascriptファイルをElectronに読ませるため、このセキュリティ警告は発生しません。安心してください。あなたのElectronアプリケーションは(今のところ)安全です!

package.json

私は先程、何も言わずにしれっとpackage.jsonに、以下の項目を追加していました。

"homepage": "./"
Enter fullscreen mode Exit fullscreen mode

もしあなたがこの項目をpackage.jsonに追加していないのならば、すぐに追加すべきです。この項目は開発時には効果をまったく発揮しませんが、ビルド時にはとても重要な項目となります。
ネタバレをしてしまうと、react-scripts buildを実行した際、出力されたindex.htmlpackage.jsonのあるディレクトリをベースに、同じくビルドされたJavascriptファイルやCSSファイルへアクセスするために設定されています。
少しわかりづらいですか?まあ要するにこの設定がないとreact-scripts buildの実行時にうまくファイルたちが関係を持てない、という具合に覚えておけばいいです。

Build and Release

01 setup

さて、いよいよ時が来て、あなたのElectronアプリケーションをリリースしたい時がやってきました。
今回はだるいのでautoupdater等の解説はしません。しかし、npmでelectron-packagerasarをdevDependenciesとしてインストールしておいてください。
かつ、これはかなり大切なことなのですが、今あなたのpackage.json"dependencies"にはreactreact-domreact-scriptsの3つが入っているはずです。(何も手を加えていないのであれば)
それをすべて、"devDependencies"に黙って移動させてしまいましょう!なに、バレませんよ。

{
  "name": "my-app",
  "version": "0.1.0",
  "private": true,
  "main": "src/electron-starter.js",
  "homepage": "./",
  "dependencies": {
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "electron:dev": "electron .",
    "dev": "npm-run-all --parallel electron:dev start"
  },
  "devDependencies": {
    "electron": "^1.8.2",
    "npm-run-all": "^4.1.3",
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "react-scripts": "1.1.1"
  }
}

Enter fullscreen mode Exit fullscreen mode

ああ、なんてこと…"dependencies"が空っぽになってしまいました。しかしご安心ください。これはこの後に効いてくる、とても効果的な、最高の設定です。

02 build

npm run buildあるいはyarn buildを実行してください。

03 packager

さて、electron-packagerの設定を忘れていました。追加しましょう。package.jsonのscriptsに以下の一文を追加します。

    "package": "electron-packager . my-app --platform=all --arch=x64 --prune --out=release --overwrite"
Enter fullscreen mode Exit fullscreen mode

さて、実行してみましょう。yarn packageあるいはnpm run packageの出番です。
時間がかかります。3分ぐらいから5分とか。気長に待ちましょう。

04 run!

パッケージングがうまくいっていれば、releaseディレクトリの中に各種バイナリが発生しているはずです!
ただし、macOSとlinuxの場合、wineがないとwin32向けビルドが発生しません。同様に、Windowsでは管理者権限のあるコンソールでないとmas(macOS)向けビルドが発生しません。(darwin向けビルドはなぜかできません)
この記事を見ているみなさんのOSは仮定しません。自分の環境で動きそうなバイナリを探してください。

わはー!動きましたね!そして、http://localhost:5000 にアクセスしても、Webサーバーが立ち上がっていないので何も表示されませんし、consoleでSecurity Warningが発生していないことを確認しましょう。静的なファイルを読み込んでいる証拠です。
…さて、これでビルドは終わりではありません。もう少しだけ、先に進んでみましょう。

05 delete unnecessary files

releaseディレクトリにぶちまけられた各種バイナリには、無駄なファイルが潜んでいます。特に、あなたがエディタに使っているのがVisual Studio Codeだった場合、.vscodeなどがそのままバイナリに入っています。
…バイナリという言葉に少々語弊がありますが。とにかく、以下のディレクトリに注目してください。

windows : /release/[app-name]-win32-x64/resources/app
darwin  : /release/[app-name]-darwin-x64/[app-name].app/Contents/Resources/app
mas     : /release/[app-name]-mas-x64/[app-name].app/Contents/Resources/app
linux   : /release/[app-name]-linux-x64/ 
Enter fullscreen mode Exit fullscreen mode

appディレクトリの中です。.gitignoreや、あろうことかsrcディレクトリまでそのまま入ってしまっています…これは問題です!消してしまいましょう。ただし、srcディレクトリには注意してください。electron-starter.jselectron-starter.jsは消してはいけません。これらは開発時と同じく、ビルド後のElectronもアクセスするファイルです。それ以外は消してしまいましょう。

app
│ package.json
│
├─build
├─node_modules
└─src
   │   application.js
   └   electron-starter.js
Enter fullscreen mode Exit fullscreen mode

掃除後のディレクトリ構造はこのような状態になっているはずです。おっと、buildnode_modulesには基本的に手を出してはいけませんよ。それからpackage.jsonを消すのもダメです。
(補足:buildディレクトリには各種mapファイルが出力されてもいます。もし実際に配布するのであれば、~.js.map~.css.mapファイルを消すのが無難でしょう)
これで実質的な最小の状態になりました。この状態でもアプリケーションが問題なく起動することを確かめておきましょう。

06 asar

さて、先程のrelease以下のそれぞれのappディレクトリはこのままでもいいですが、Electronはasarでappディレクトリを圧縮することを推奨しています。一つ前のディレクトリにまでコマンドラインで戻り、appをasarで圧縮します。

> asar pack app app.asar
Enter fullscreen mode Exit fullscreen mode

のち、appディレクトリを削除します。
この状態でアプリケーションが問題なく起動することを確かめてください。

07 zipping application

あとは、releaseディレクトリ以下のそれぞれのディレクトリをZIPかなにかで圧縮すれば、もう配布可能なバイナリになりました。おめでとうございます!

ただし、darwin(mas)にはアプリに署名をするという大切な工程もありますが、今回は無視しています。各位で調べてください。

Explanation Build and Release

Why moved all package from dependencies to devDependencies?(it's important!)

これはかなり奇妙な行動に思えたはずです。なぜすべてのパッケージをdevDependenciesに移動したか?それは配布後のパッケージのサイズに関係してきます。
まず大前提をお話しましょう。今回特に、react-scriptsはかなり大きなパッケージであるということが関係してきます。react-scriptsの関係するパッケージはまとめて100MBを超えています。このパッケージを配布バイナリに含めるとなると、それだけで数百MBもサイズが膨れ上がってしまいます。

ところで、アプリケーションとして配布する時、パッケージング前にreact-scripts buildを実行したことを覚えていますか?build実行時にreactreact-domそれらをすべてまとめてbuildディレクトリ以下のファイルにまとめて梱包して出力してくれるのです。これらは裏でwebpackが動いてくれているからなのですが。
まあそれはさておいて、この時点でbuildディレクトリの中のファイルはnode_modulesとは(ほぼ)関係なしに動いてくれるようになりました。

つまり、electron-packagerを使ってElectronアプリを梱包する時、もうreact-scriptsは必要ないのです!また、それはreactreact-domに対しても言えることです。今後導入するであろうreduxreact-reduxredux-sagaなどのパッケージに対しても同様です。開発時とビルド時にのみ必要なパッケージは、すべてdevDependencies(開発時の依存関係)に入れてしまいましょう。
devDependenciesに突っ込んだパッケージは、electron-packagerがすべて取り除いてくれます。

ただし、例外もあります。Electronではrenderプロセス――つまりsrc/index.js以下のどこかのタイミング――でNode.jsとそれらを使用したパッケージにアクセスできることをご存知でしょうか?electron.remote.require()のことです。
もしあなたがNode.jsを使用したパッケージをelectron.remote.requireを使用して呼び出していた場合、そのパッケージはdevDependenciesに入れてはなりません
なぜならば、そのパッケージは実行時にnode_modulesから呼び出されるからです。

同様に、今回で言えばapplication.jsなど、Electronが直接読むスクリプトの中で呼び出しているパッケージも同様にdevDependenciesに入れてはいけません。たとえば、electron-about-windowなどです。
以上のことはかなり注意すべきことです。覚えておきましょう。

Why don't use --ignore option in electron-packager?

…これは本当に私もわからないのですが、Windowsから--ignoreオプションを使おうとすると、どうしてもエラーが起きてしまって、パッケージングできないという問題があります…未だに不明なのですが、linuxやmacOSをお使いの皆さんなら使えるはずです。都度使ってみてください。正規表現パターンを使用してパッケージング時に不要なファイルを削除できます。また、不要なファイルを全て削除できるなら--asarオプションでasarに圧縮するところまで自動でやってくれます。
私はそれがどうしてもできないので、自分でビルドスクリプトを書きました。みなさんも勝手に書くべきです。

Conclusion

今回、webpackの設定ファイルを一切書いてないことに気付きましたか?かなり込み入ったことをしない限り(ejectを必要としない限り)は、webpackの設定ファイルを一切書くことなくElectronアプリを書けることがわかりましたね。さらにcreate-react-appでは型チェックツールであるFlowもそのまま使うこともできます。
宣伝ではありますが、これとほぼ同様のやり方でTsuruも開発されています。

この記事はHow to build an Electron app using create-react-app. No webpack configuration or “ejecting” necessary.
をベースに、さらに踏み込んだ所まで解説した記事です。

Update

Foremanをnpm-run-allに置換しました。これによりProcfileがなくても実行できるようになり、実行スクリプトがすべてpackage.jsonscripts内におさまり、管理しやすくなります。

💖 💪 🙅 🚩
origamium
Origami

Posted on March 5, 2018

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related