LoginSignup
23
26

More than 5 years have passed since last update.

Electron+React+Babel+Gulp環境構築

Posted at

はじめに

JSONな設定ファイルを編集する環境としてExcelを使ったけど、
ExcelからJSON吐き出すのが面倒だった

JSONが扱いやすい環境としてWebアプリを調べたところElectronが作りやすそうだったのでお試し.

Web界隈は環境の変化が激しいせいか導入が難しかった

つまずいたこと

  • React公式チュートリアルはtype="text/babel"でjsx変換してる
    • 実行前に変換済みファイル吐いたほうがミス探しやすいと思う
  • gulp以外の変換ツールだとrequire('electron')で怒られることがあった。
    • 正しい設定あると思うけど正解探すのが大変だった
  • TypeScript使ったほうが作りやすいけど設定がさらに複雑に
    • gulpなにそれ状態からElectron+React+Babel+Gulp+TypeScriptとか死ねる
    • 一通り基本設定が出来てからTypeScript対応したほうが無難っぽい

開発環境

Windows10 + Git Bash

使用ツール

  • Electron
    • node.js + Chromiumなアプリ開発環境
  • React
    • Facebook製MVCのView関連ライブラリ
  • Photon
    • Electronアプリに綺麗な見た目を
  • Babel
    • React向けファイル変換
  • Gulp
    • ファイル変換の自動化

環境構築

node.jsインストール

windowsでのnode.js環境構築はnodistを使った。
インストーラーがあるので楽ちん

Electronインストール

$ npm install -g electron

photon

npmリポジトリツリーにいないようなのでgithubURLで

$ npm install https://github.com/connors/photon

React

$ npm install --save-dev react react-dom

Babel

$ npm install -g babel-cli
$ npm install --save-dev babel-preset-es2015

ファイル指定変化はこんな感じ

$ babel before.js -o after.js

ファイル修正のたびにbabelコマンドを打つのは大変なのでgulpを使う

gulp

-g付きでgulpコマンドが使えるように

$ npm install -g gulp-babel

プロジェクトフォルダにも

$ npm install --save-dev gulp

サンプルアプリ

起動時にconfig.jsonを読み込んでUIに反映する

アプリフォルダ構成

app/
 |--build/              ※変換後jsファイル置き場
 |--data/
 |   |--config.json
 |--lib/
 |   |--main.js         ※Electronアプリメイン
 |   |--fileUtil.js     ※ファイル操作関連
 |--node_modules/       ※ローカルインストールしたnode.jsモジュール群
 |   |--photon/
 |   |--react/
 |   |--react-dom/
 |   |--...
 |--view/               ※Electronアプリ 描画群
 |   |--index.html
 |   |--index.js        ※UI構築メイン babelで変換される
 |   |--component.js    ※UI構築サブ   babelで変換される
 |--.babelrc            ※babel設定
 |--gulpfile.js         ※gulp設定
 |--package.json        ※electronアプリ設定

アプリ構築

.babelrcの設定

$ cat .babelrc
{ "presets": ["react"] }

gulpfile.jsの設定

$ cat gulpfile.js
var gulp = require('gulp');
var babel = require('gulp-babel');

gulp.task('babel', function () {
  return gulp.src('./view/*.js') //view/以下の.jsを変換対象に
    .pipe(babel())
    .pipe(gulp.dest('./build')); //build/に出力
});

gulp.task('watch', function () {
  gulp.watch(src, ['babel']);
});

gulp.task('default', ['babel']);

package.jsonの設定

$ cat package.json
{
  "name": "electron",
  "version": "1.0.0",
  "description": "",
  "main": "lib/main.js",
  "scripts": {
    "start": "electron .",
  },
  "keywords": [],
  "author": "",
  "dependencies": {
    "react": "^0.14.0",
    "react-dom": "^0.14.0"
  },
  "devDependencies": {
    "babel-plugin-transform-react-jsx": "^6.8.0",
    "gulp": "^3.9.1",
    "gulp-babel": "^6.1.2"
  }
}

lib/main.js

本家サンプルのまんま

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

// 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 win

function createWindow () {
  // Create the browser window.
  win = new BrowserWindow({width: 800, height: 600})

  // and load the index.html of the app.
  win.loadURL(`file://${__dirname}/../view/index.html`)

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

  // Emitted when the window is closed.
  win.on('closed', () => {
    // 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.
    win = 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', () => {
  // On macOS 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', () => {
  // On macOS 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 (win === null) {
    createWindow()
  }
})

lib/fileUtil.js

const fs = require("fs");

//JSONファイルをテキストのまま取得
exports.readJsonPlain = (path, callback) => {
  fs.readFile(path, 'utf8', (err, data) => {
    if(!err)
    {
      callback(data);
    }
    else {
      console.log(err);
    }
  });
}

//JSONファイルをオブジェクト化して取得
exports.readJson = (path, callback) => {
  fs.readFile(path, 'utf8', (err, data) => {
    if(!err)
    {
      callback( JSON.parse(data) );
    }
    else {
      console.log(err);
    }
  });
}

view/index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <!-- photon css読み込み -->
    <link rel="stylesheet" type="text/css" href="../node_modules/photon/dist/css/photon.css" />
    <title>Electron Sample</title>
  </head>
  <body>
  <div class="window">
    <!-- ツールバー -->
    <header class="toolbar toolbar-header">
    </header>

    <div class="window-content">
      <div class="pane-group">
        <!-- 左側サイドバー idで要素特定してReactで挿入する-->
        <div class="pane pane-sm sidebar" id="sidebar-left">
        </div>

        <!-- 右側サイドバー -->
        <div class="pane pane-sm sidebar" id="sidebar-right">
        </div>
      </div>
    </div>
   </div>

   <script src="../node_modules/react/dist/react.js"></script>
   <script src="../node_modules/react-dom/dist/react-dom.js"></script>
   <!-- 変換後jsを読みこませるのでbuild/以下のjsを指定 -->
   <script src="../build/index.js"></script>
  </body>
</html>

view/index.js

//ファイル読み込みはメインプロセス側で動作させてみる
const remote = require('electron').remote;
const fileUtil = remote.require('./fileUtil');

const React = require('react');
const ReactDOM = require('react-dom');
//変換後jsを読み込むのでbuild/以下のファイルを指定
const SidebarLeft = require('../build/sidebar-left');

document.addEventListener( "DOMContentLoaded", () => {
  //file path はアプリルートフォルダから相対で。
  //`file://${__dirname}/../data/config.json`はエラーになった
  fileUtil.readJson(`data/config.json`, (data) => {
    const sidebar = document.getElementById('sidebar-left');
    ReactDOM.render(
      <SidebarLeft data={data}/>,
      sidebar );
  });
});

view/sidebar-left.js

const React = require("react");

var SidebarLeft = React.createClass({
  render: function() {
    var nodes = [];
    for(var v in this.props.data)
    {
      nodes.push(
        <h1>{v}</h1>
      );
    }
    return (
      <div className="test">{nodes}</div>
    );
  }
});

module.exports = SidebarLeft;

参考にしたURL

23
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
26