create-react-appを使用したElectronアプリケーションの構築

webpackの設定や「イジェクト」は必要ありません。

最近、create-react-appを使用してElectronアプリを作成しましたWebpackをいじくり回したり、アプリを「イジェクト」したりする必要もありませんでした。私がこれをどのように達成したかを説明します。

create-react-appを使用すると、webpack構成の詳細が非表示になるため、私はそのアイデアに惹かれました。しかし、Electronとcreate-react-appを一緒に使用するための既存のガイドを検索しても効果がなかったので、自分で調べてみました。

焦りを感じている場合は、すぐに飛び込んで私のコードを見ることができます。これが私のアプリのGitHubリポジトリです。

始める前に、ElectronとReactについて、そしてcreate-react-appがなぜこんなに素晴らしいツールなのかをお話ししましょう。

電子と反応

ReactはFacebookのJavaScriptビューフレームワークです。

ユーザーインターフェイスを構築するためのJavaScriptライブラリ-React

ユーザーインターフェイスを構築するためのJavaScriptライブラリfacebook.github.io

また、Electronは、JavaScriptでクロスプラットフォームのデスクトップアプリを構築するためのGitHubのフレームワークです。

電子

JavaScript、HTML、CSSを使用してクロスプラットフォームのデスクトップアプリを構築します。electronic.atom.io

ほとんどの場合、React開発に必要な構成にwebpackを使用します。webpackは、ほとんどのReactコミュニティがGulpやGruntなどの代替手段よりも採用している構成およびビルドツールです。

構成のオーバーヘッドはさまざまで(これについては後で詳しく説明します)、利用可能なボイラープレートとアプリケーションジェネレーターは多数ありますが、2016年7月にFacebookIncubatorがツールをリリースしました。create-react-app これは、ほとんどの設定を隠し、開発者の使用単純なコマンド、といったことができますnpm startし、npm run build自分のアプリを実行し、構築するために。

排出とは何ですか、なぜそれを避けたいのですか?

create-react-appは、典型的なReactのセットアップについて特定の仮定をします。これらの仮定が適切でない場合は、アプリケーションをイジェクトするオプションがあります(npm run eject)。アプリケーションをイジェクトすると、create-react-appのカプセル化されたすべての構成がプロジェクトにコピーされ、必要に応じて変更できる定型的な構成が提供されます。

しかし、これは片道の旅です。イジェクトを元に戻して戻ることはできません。create-react-appのリリースは(この投稿の時点で)49あり、それぞれが改善されています。ただし、排出されたアプリケーションの場合、これらの改善を放棄するか、それらを適用する方法を理解する必要があります。

排出される構成は、7つのファイルにまたがる550行を超えています(この投稿の時点で)。私はそれをすべて(まあ、実際にはほとんど)理解していませんし、理解したくありません。

目標

私の目標は単純です:

  • Reactアプリのイジェクトを回避する
  • 接着剤を最小限に抑えて、ReactとElectronを連携させます
  • Electronおよびcreate-react-app / Reactによって作成されたデフォルト、仮定、および規則を保持します。(これにより、そのような規則を想定/必要とする他のツールを簡単に使用できるようになります。)

基本レシピ

  1. create-react-app基本的なReactアプリケーションを生成するために実行します
  2. 実行 npm install --save-dev electron
  3. 追加main.jsからelectron-quick-start(私たちはそれに名前を変更しますelectron-starter.js明確にするため、)
  4. (webpack-dev-server)を使用するようにmainWindow.loadURL(in electron-starter.js)への呼び出しを変更しlocalhost:3000ます
  5. のメインエントリを追加package.jsonしますelectron-starter.js
  6. Electronを開始する実行ターゲットを追加します package.json
  7. npm start に続く npm run electron

手順1と2は非常に簡単です。手順3と4のコードは次のとおりです。

const electron = require('electron'); // Module to control application life. const app = electron.app; // Module to create native browser window. const BrowserWindow = electron.BrowserWindow; const path = require('path'); const url = require('url'); // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. let mainWindow; function createWindow() { // Create the browser window. mainWindow = new BrowserWindow({width: 800, height: 600}); // and load the index.html of the app. mainWindow.loadURL('//localhost:3000'); // Open the DevTools. mainWindow.webContents.openDevTools(); // Emitted when the window is closed. mainWindow.on('closed', function () { // Dereference the window object, usually you would store windows // in an array if your app supports multi windows, this is the time // when you should delete the corresponding element. mainWindow = null }) } // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.on('ready', createWindow); // Quit when all windows are closed. app.on('window-all-closed', function () { // On OS X it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== 'darwin') { app.quit() } }); app.on('activate', function () { // On OS X it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (mainWindow === null) { createWindow() } }); // In this file you can include the rest of your app's specific main process // code. You can also put them in separate files and require them here.

(要旨)

そして、ステップ5と6の場合:

{ "name": "electron-with-create-react-app", "version": "0.1.0", "private": true, "devDependencies": { "electron": "^1.4.14", "react-scripts": "0.8.5" }, "dependencies": { "react": "^15.4.2", "react-dom": "^15.4.2" }, "main": "src/electron-starter.js", "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject", "electron": "electron ." } }

(要旨)

手順7でnpmコマンドを実行すると、次のように表示されます。

Reactコードにライブ変更を加えることができ、実行中のElectronアプリに反映されていることを確認できます。

これは開発には問題なく機能しますが、2つの欠点があります。

  • プロダクションは使用しませんwebpack-dev-server。Reactプロジェクトのビルドからの静的ファイルを使用する必要があります
  • (小さい)両方のnpmコマンドを実行するのが面倒

本番環境と開発環境でloadURLを指定する

In development, an environment variable can specify the url for mainWindow.loadURL (in electron-starter.js). If the env var exists, we’ll use it; else we’ll use the production static HTML file.

We’ll add a npm run target (to package.json) as follows:

"electron-dev": "ELECTRON_START_URL=//localhost:3000 electron ."

Update: Windows users will need to do the following: (thanks to @bfarmilo)

”electron-dev”: "set ELECTRON_START_URL=//localhost:3000 && electron .”

In electron-starter.js, we’ll modify the mainWindow.loadURL call as follows:

const startUrl = process.env.ELECTRON_START_URL || url.format({ pathname: path.join(__dirname, '/../build/index.html'), protocol: 'file:', slashes: true }); mainWindow.loadURL(startUrl);

(Gist)

There is a problem with this: create-react-app (by default) builds an index.html that uses absolute paths. This will fail when loading it in Electron. Thankfully, there is a config option to change this: set a homepage property in package.json. (Facebook documentation on the property is here.)

So we can set this property to the current directory and npm run build will use it as a relative path.

"homepage": "./",

Foremanを使用したReactおよびElectronプロセスの管理

便宜上、私はしたくない

  1. React開発サーバーとElectronプロセスの両方を起動/管理します(どちらか一方を扱いたい)
  2. React開発サーバーが起動するのを待ってからElectronを起動します

フォアメンは優れたプロセス管理ツールです。追加できます、

npm install --save-dev foreman

そして、以下を追加します Procfile

react: npm startelectron: npm run electron

(要旨)

それは(1)を扱っています。(2)の場合electron-wait-react.js、React devサーバーの起動を待機してからElectronを起動する単純なノードスクリプト()を追加できます。

const net = require('net'); const port = process.env.PORT ? (process.env.PORT - 100) : 3000; process.env.ELECTRON_START_URL = `//localhost:${port}`; const client = new net.Socket(); let startedElectron = false; const tryConnection = () => client.connect({port: port}, () => { client.end(); if(!startedElectron) { console.log('starting electron'); startedElectron = true; const exec = require('child_process').exec; exec('npm run electron'); } } ); tryConnection(); client.on('error', (error) => { setTimeout(tryConnection, 1000); });

(要旨)

注:フォアマンは、さまざまなタイプのプロセスのポート番号を100オフセットします。(ここを参照してください。)したがって、electron-wait-react.js100を引いて、React開発サーバーのポート番号を正しく設定します。

次に、 Procfile

react: npm startelectron: node src/electron-wait-react

(要旨)

最後に、我々は中に実行するターゲット変更しますpackage.json交換するelectron-devとし:

"dev" : "nf start"

そして今、私たちは実行することができます:

npm run dev
更新(1/25/17):いくつかのユーザーコメント(こことここ)に応じて、次のセクションを追加しました。彼らはreactアプリ内からElectronにアクセスする必要があり、単純なrequireまたはimportはエラーをスローします。以下に1つの解決策を示します。

Reactアプリから電子にアクセスする

Electronアプリには、Electronホスト/ラッパーとアプリの2つの主要なプロセスがあります。場合によっては、アプリケーション内からElectronにアクセスしたいことがあります。たとえば、ローカルファイルシステムにアクセスしたり、Electronを使用したりできますipcRenderer。ただし、次のようにすると、エラーが発生します

const electron = require('electron') //or import electron from 'electron';

このエラーなど、GitHubやStackOverflowのさまざまな問題でこのエラーについていくつかの議論があります。ほとんどのソリューションはwebpack構成の変更を提案しますが、これにはアプリケーションの取り出しが必要になります。

ただし、簡単な回避策/ハックがあります。

const electron = window.require('electron');
const electron = window.require('electron'); const fs = electron.remote.require('fs'); const ipcRenderer = electron.ipcRenderer;

まとめ

便宜上、上記のすべての変更があり、各ステップのタグが付いたGitHubリポジトリがあります。ただし、create-react-appを使用するElectronアプリケーションをブートストラップする作業はそれほど多くありません。(この投稿は、2つを統合するために必要なコードと変更よりもはるかに長くなります。)

また、create-react-appを使用している場合は、私の投稿、WebStormでのデバッグテストおよびcreate-react-appを確認することをお勧めします。

読んでくれてありがとう。justideas.ioで私の投稿をもっとチェックできます

更新(2017年2月2日)。読者のCarlVitulloが、GitHubでnpm start代わりに使用することを提案npm run devし、変更を加えたプルリクエストを送信しました。これらの調整は、このブランチで利用できます。