electronでReactを使うためのwebpackの設定

はじめに

electron。javascriptでデスクトップアプリケーションを作れるフレームワーク
使ってみたかったけど、なかなかよい作りたいものも思いつかずsample demo appで遊ぶくらいでした。 最近ちょっと良いアイデアも思いついたので、思い切ってelectronを触ってみました。 また合わせてreactの勉強もしてみたかったので、React x electronの環境構築の備忘録を残しておこうと思います。 reduxは使いません。まだ扱えきれるレベルじゃないないので。。。

ポイント

  • node_modulesを呼び出せるelectronのjsをどうやってバンドルするの?
  • main・rendererプロセス用の2種類のjsが必要

electronのjsはfsなどnodeで使えるモジュールを呼び出せるので、
webのReactと同様にimportできるのか。。。

またelectronには、mainプロセスとrendererプロセスが存在し、それぞれにjsファイルがあるのに対して、多分普通のReactのプロジェクトでは、htmlで読み込むのは、一つのjsだけ。
この違いをどうやって、対応するか。。。

結論急げば、webpackの設定でどちらの問題も解決できます。まじ優秀!!

流れ

  1. electronのインストール
  2. electron起動
  3. reactの環境構築
  4. build失敗の確認
  5. webpackの設定(nodeモジュールをimportさせる)
  6. webpackの設定(2つのjsファイルを作成)
  7. buildしてみる

1. electronのインストール

これだけ。はは、便利

npm install -g electron 

2. electron起動

プロジェクトを作って、mainプロセス用のjsと、rendererプロセスで必要なhtmlとそれが呼び出すrenderer.jsを作成します。 js,htmlが用意できたら、electronコマンドでmain.jsを指定します

mkdir sampleApp
cd sampleApp
npm init -y
touch main.js     
touch index.html   
electron main.js

作成したhtml,jsは、tutorialのWriting Your First Electron Appからコピペしてます。

f:id:poppon555:20180402195318p:plain

こんな感じ。

3. reactの環境構築

他の色々なサイトに導入方法は書いてあるが、自分は【Reactではじめるフロントエンド開発入門】1 を参考にさせてもらいました。npm, yarn両方の構築方法が丁寧に書かれてあるので、勉強になりました。

// react
npm install react react-dom

// webpack
npm install -D webpack webpack-cli

// babel
npm i babel-core -D
npm i babel-preset-es2015 -D
npm i babel-preset-react -D
npm i babel-loader -D

webpackの設定も最低限

// webpack.config.js
const webpack = require("webpack");
const path = require('path');

const config = {
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: path.resolve(__dirname, 'node_modules'),
      loader: 'babel-loader',
      query:{
        presets: ['react', 'es2015'],
      }
    }]
  }
};

module.exports = config;

npm buildでapp.jsができると、後はindex.htmlでロードすればおしまい。

f:id:poppon555:20180402195006p:plain

4. build失敗の確認

では、webのプロジェクト同じやり方でjsファイルを作成しトランスパイルさせてみます

nodeモジュールfsを利用した以下のファイルを作成する。

import React, { Component } from 'react';
import ReactDom from 'react-dom';
// 追加: nodeのモジュールfsをimportする
import fs from 'fs'

export default class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      file : ''
    }
  }

  componentWillMount() {
    // fsモジュールを使用する
    this.setState({file:fs.readFileSync('index.html','utf8')})
  }

  render() {
    return <h1>{this.state.file}</h1>
  }
}

ReactDom.render(
  <App />,
  document.getElementById('root')
);

npm buildするとこちら。

f:id:poppon555:20180402195010p:plain

はい、失敗します! fsが読み込めませんと。 なので、これの対応がwebのときと異なり必要となってきます。

5. webpackの設定(nodeモジュールをimportさせる)

google先生に聞いてみると、ここに electron + webpack + react + sass ほとんど書いてました。targetプロパティなるものが、重要ということ。 webpack公式targetによると、targetには,electron-mainとelectron-rendererを 指定できるようなので、これを使いましょう。

では、targetプロパティを追加して、electron用であることを指定します。

// webpack.config.js
const webpack = require("webpack");
const path = require('path');

const config = {
  entry: './src/app.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'app.js'
  },
  target:'electron-renderer',   // 追加 renderer用
  module: {
    rules: [{
      test: /\.js$/,
      exclude: path.resolve(__dirname, 'node_modules'),
      loader: 'babel-loader',
      query:{
        presets: ['react', 'es2015'],
      }
    }]
  }  
};

module.exports = config;

6. buildしてみる

f:id:poppon555:20180402195016p:plain

build成功

f:id:poppon555:20180402195021p:plain

electron起動

7. webpackの設定(2つのjsファイルを作成)

もう一つ課題が残っていました。
mainプロセスとrendererプロセス用の二つのjsをトランスパイルさせましょう。 renderer用のみトランスパイルするなら特に不要な作業だが、main.jsだけはcommonjs記法を採用するという
気持ちの悪い感じがするので、自分はmain.jsもトランスパイルさせました。

設定方法は実に簡単。
main用とrenderer用の設定を記述して、配列にしたものをconfigに格納するだけになります。

// webpack.config.js
const config = [
  {
    // mainプロセス用
    entry: './src/main/main.js',
    output: {
      path: path.resolve(__dirname, 'dist/main'),
      filename: 'main.js'
    },
    target: 'electron-main',
    module: {
      rules:[
      {
        test: /\.js$/,
        exclude: path.resolve(__dirname, 'node_modules'),
        loader: 'babel-loader',
        query: {
          presets: ['react', 'es2015']
        }
      }
      ]
    }
  },
  {
    // renderer用
    entry: './src/renderer/renderer.js',
    output: {
      path: path.resolve(__dirname, 'dist/renderer'),
      filename: 'renderer.js'
    },
    target: 'electron-renderer',
    module: {
      rules:[
      {
        test: /\.js$/,
        exclude: path.resolve(__dirname, 'node_modules'),
        loader: 'babel-loader',
        query: {
          presets: ['react', 'es2015'],
          plugins:['transform-class-properties']
        },
      }
      ]
    }
  }
]

その結果、main用とrenderer用の二つのjsファイルを作成されます。

dist
├─main
│      main.js
│
└─renderer
        index.html
        renderer.js

最後に

webpackの設定は奥が深い...。
フロントエンドの勉強は学習範囲が多すぎて、どこか不安になってしまう。
これが仕事となると、本当に効率よい学習が必要なんだろうな~と思う。 けど、jqueryも使う気がしないのは確かだ。