Electron + React.js でアプリ開発をする。

erectron + react を使ったアプリ開発のプロジェクト構成について。

TypeScript を利用する場合についても記載しようかと思ってたんですが、
ちょっとボリュームが多くなりそうだったので、そちらはまた今度の機会に記載しようかと思います。

また今回、作ったものはテンプレートとして下記リポジトリで公開しています。
https://gitlab.com/tyabuta/template-electron-reactjs

フロントエンド開発

最近、またまたエレクトロンアプリを作ろうと思って、再度調べ直したので
最小限のプロジェクトの作り方を記録しておきます。

フロントエンド界隈は移り変わりが激しく、去年の記事でも古いという状況が多々あり大変だす。

Electron + React.js + Babel + Webpack

今回は React.js を使いたかったので、
React.js は jsx というjs拡張言語を使うため、トランスパイラが必要になってくる。

Electron + React.js + Babel + Webpack という構成が一番スマートかと自分は思っています。

(Babel自体はjsx用という訳ではなく、es5に変換したりするのが本来の用途みたいですが、jsxのトランスバイルもやってくれているみたいです。)

ディレクトリ構成

最初に最終的にどんなディレクトリ構成になるのかを記載しておきます。

.
├── index.js
├── node_modules
├── package.json
├── public
│   ├── build
│   │   ├── app.js
│   │   ├── app.js.map
│   │   ├── vendor.js
│   │   └── vendor.js.map
│   └── index.html
├── src
│   └── index.js
└── webpack.config.js

public/build ディレクトリは、src配下のファイルがwebpackによりバンドルされたファイルが出力されます。

設定しないといけないファイルは、

  • package.json
  • webpack.config.js

最低限必要なコーディングファイルは、

  • index.js
  • public/index.html
  • src/index.js

プロジェクト作成

npm init でpackage.jsonを作成したあとは、以下のパッケージをインストールする。

npm install

# electron
npm install -S electron

# react.js
npm install -S react react-dom

# babel
npm install -D babel-core babel-loader babel-preset-env 
# react用のやつ
npm install -D babel-preset-react

# webpack
npm install -D webpack

設定ファイル類

package.json

package.json はこんな感じになります。

"main" にかかれているindex.jsファイルが、
electronでのメインプロセス側の処理になります。

"dependencies" には、electronとreactが入って、
"devDependencies" には、babelとwebpackがインストールされている状態ですね。

{
  "name": "electron-react-babel-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "electron": "^1.8.2",
    "react": "^16.2.0",
    "react-dom": "^16.2.0"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-env": "^1.6.1",
    "babel-preset-react": "^6.24.1",
    "webpack": "^3.11.0"
  }
}

webpack.config.js

こちらが、webpack.config.js です。

const path = require('path');
const webpack = require('webpack');

module.exports = {
    target:"electron-renderer",
    devtool: 'source-map',

    entry: {
        app: './src',
    },

    // バンドルファイルの出力場所
    output: {
        filename: "[name].js",
        path: path.resolve(__dirname, 'public/build')
    },

    resolve: {
        modules: [
            // srcディレクトリをimport解決のrootに設定
            path.resolve(__dirname, "src"),
            "node_modules",
        ],
        extensions: [".js", ".jsx"]
    },

    module: {
        rules: [
            {
                // react jsx のトランスパイル設定
                test: /\.js?$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader', options: {presets: [['env', {'modules': false}], "react"]}
                    }
                ]
            }
        ]
    },

    plugins: [
        // node_modules配下を、vendor.js としてバンドルする。
        new webpack.optimize.CommonsChunkPlugin({
            name: "vendor",
            minChunks: function (module) {
                return module.context && module.context.indexOf("node_modules") !== -1;
            }
        })
    ]
};

webpackでは、babelを使って、jsxをトランスパイルするのと、
バンドルして一つのjsファイルにまとめる処理を行います。

ただ、npmでインストールしたパッケージと、自分で書くコードは別にしておいた方が効率が良いかと思いますので、
vendor.jsという形で別ファイルにバンドルするように設定しています。

webpackで処理しているのは、electronのレンダープロセスで使うjsファイルです。
なので、"target"に"electron-renderer"を設定しているところが、重要です。

この設定をする事で、electronのメインプロセス側と通信する処理が正常にビルドできるようになります。
(クリップボード機能とか)

resolveに、srcディレクトリを追加しているので、
インポート文を書くときは、srcディレクトリからの相対PATHで記載することができます。

コーティング

index.js

ほぼ、electron公式に書いてあるサンプルソースそのままです。

loadURLのところで、
public/index.html をブラウジングするように書き換えているだけになります。

const {app, BrowserWindow} = require('electron');
const path = require('path');
const url = require('url');

let win;

function createWindow() {
    win = new BrowserWindow({width: 800, height: 600});
    win.loadURL(url.format({
        pathname: path.join(__dirname, 'public', 'index.html'),
        protocol: 'file:',
        slashes: true
    }));

    // Open the DevTools.
    win.webContents.openDevTools();

    win.on('closed', () => {
        win = null
    })
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit()
    }
});

app.on('activate', () => {
    if (win === null) {
        createWindow()
    }
});

public/index.html

React.js のルート要素になるdivタグと、
public/build配下に出力されたファイルを、scriptタグで読み込んでいるだけです。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Hello World!</title>

        <script src="build/vendor.js"></script>
    </head>

    <body>
        <div id="root"></div>
        <script src="build/app.js"></script>
    </body>
</html>

src/index.js

React.js のハロワのサンプルソースです。

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('root')
);

起動してみる

webpackでバンドル。

npx webpack
Hash: 352483fa346b49e23bd5
Version: webpack 3.11.0
Time: 1304ms
        Asset       Size  Chunks                    Chunk Names
       app.js  886 bytes       0  [emitted]         app
    vendor.js     721 kB       1  [emitted]  [big]  vendor
   app.js.map  491 bytes       0  [emitted]         app
vendor.js.map     853 kB       1  [emitted]         vendor
  [13] ./src/index.js 175 bytes {0} [built]
    + 25 hidden modules

エレクトロン起動!!

npx electron .

おしまい

フロントエンド界隈のトレンドは本当にすぐ変わる。

去年も似たように調べたんだけど、
やっぱり、当時と設定の仕方が若干変わっていたりしている気がします。

2件のコメント

コメントを残す