29
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Electron + Express + React で Web・デスクトップ共通のアプリを作る

Posted at

Electron + Express.js + React を使ってWebアプリとデスクトップアプリを共通のコードで作ってみたいと思います。

今回作成したコードは github に置いてあります。


参考にしたサイト


想定しているターゲット

あまり無いとは思いますが,普段,サーバに繋がる環境ではブラウザからアクセスしてアプリを実行するけど,稀にサーバに繋がらないスタンドアロンの環境でもアプリを実行するようなケースを想定しています。


動作確認環境

  • OS macOS Mojave
  • Electron 4.0.6
  • Express.js 4.16.0
  • React 16.8.6
  • Node.js v11.6.0

それでは早速ですが,アプリの雛形生成に express-generator を使用するので、インストールを行います。

$ npm install -g express-generator

"sample-app" という名前でアプリケーションを作成します。

$ express sample-app
$ cd sample-app

必要なパッケージをインストールします。

$ npm install

この時点で動作確認してみます。以下のコマンドを実行して

$ npm start

ブラウザで localhost:3000 にアクセスすると以下のような画面が表示されるはずです。

01.png

Electron のセットアップ

以下のコマンドで Electron をインストールします。

$ npm install --save-dev electron

続いて、app.js と同じ場所に main.js を作成します。

main.js
// Start Express
const expressApp = require('./app')
expressApp.listen(3000, '127.0.0.1')

// Electronのモジュール
const electron = require('electron')

// アプリケーションをコントロールするモジュール
const app = electron.app

// ウィンドウを作成するモジュール
const BrowserWindow = electron.BrowserWindow

// メインウィンドウはGCされないようにグローバル宣言
let mainWindow

// 全てのウィンドウが閉じたら終了
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// Electronの初期化完了後に実行
app.on('ready', () => {
  // メイン画面の表示。ウィンドウの幅、高さを指定できる
  mainWindow = new BrowserWindow({ width: 800, height: 600 })
  mainWindow.loadURL('http://127.0.0.1:3000')

  // ウィンドウが閉じられたらアプリも終了
  mainWindow.on('closed', () => {
    mainWindow = null
  })
})

package.json に以下の一行を追加します。

  "main": "main.js",

以下のコマンドを実行して動作確認します。

$ npx electron .

以下のような画面が表示されれば成功です。

02.png

npm-run-all で Web と Electron を同時に起動する

必須ではありませんが,一つのコマンドで Web サーバと Electron のアプリを同時に起動できると便利な事が多いのでここで設定しておきます。

npm-run-all という複数のタスクを同時に起動するパッケージを使用して、Web と Electron を同時に起動できるようにします。

まず以下のコマンドで npm-run-all をインストールします。

$ npm install --save-dev npm-run-all

次に package.json を以下のように修正します。

package.json
  "scripts": {
    "start": "node ./bin/www",
    "electron": "electron .",
    "dev": "npm-run-all --parallel electron start",
    "package": "npx electron-packager . sample-app --platform=darwin --arch=x64 --overwrite"
  }

これで以下のコマンドで Web サーバと Electron アプリが同時に起動できるようになりました。

$ npm run dev

React の追加

下記のコマンドで React のパッケージをインストールします。

npm install --save react react-dom

Jade は使用しないので削除しておきます。

npm uninstall --save jade

まずは Hello World を作成してみます。

public/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>sample-app</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/javascript" src="javascripts/app.js" charset="utf-8"></script>
  </body>
</html>

"Hello, world" を返す React のコンポーネントを作成します。

src/index.js
import React from 'react'
import ReactDOM from 'react-dom'

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

これでソース自体の作成は完了です。後は実行するための設定を行います。

不要な処理・ファイルの削除

"express-generator" が作成したファイル・コードの中で不要になったものを削除します。

$ rm routes/*.js
$ rm -rf views

app.js の以下の行を削除します。

app.js
var indexRouter = require('./routes/index')
var usersRouter = require('./routes/users')
// view engine setup
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'jade')
app.use('/', indexRouter)
app.use('/users', usersRouter)

Babel と Webpack の設定

JSX から JavaScript への変換に使用する Babel のインストールと設定行います。

$ npm install --save-dev babel babel-core babel-loader@7 babel-preset-env babel-preset-react

.babelrc に以下の内容を記述します。

.babelrc
{
  "presets": ["env", "react"]
}

Webpack のインストール

$ npm install --save-dev webpack webpack-cli
$ npm install --save-dev babel-loader css-loader style-loader
package.json
  "build": "webpack -d"
wabpack.config.js
const path = require('path')
module.exports = {
  mode: 'development',
  entry: {
    app: path.join(__dirname, 'src/index.js')
  },
  output: {
    path: path.join(__dirname, 'public/javascripts'),
    filename: '[name].js'
  },
  devtool: 'inline-source-map',
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader'
      }, {
        test: /\.css$/,
        loader: ['css-loader', 'style-loader']
      }
    ]
  }
}

この時点で以下のコマンドを実行すると

$ npm run build
$ npm run dev

以下のような画面が表示され、またブラウザで localhost にアクセスすると同様な画面が表示されると思います。

hello_world.png

React と Express の連携

月並みですが,サーバ側(Express)でおみくじの結果を生成して画面に表示する処理を実装してみたいと思います。

クライアントからサーバへのAjax通信には "SuperAgent" を使うのではじめにインストールしておきます。

$ nom install --save superagent

サーバ側の実装

"/api/kuji" にアクセスされると "{result: '大吉'}" のような JSON データを返す API を実装します。

routes/kuji.js
var express = require('express')

var router = express.Router()

const kuji = ['大吉', '', '']

const get_kuji = () => {
  const rnd = Math.floor(Math.random() * kuji.length)
  return kuji[rnd]
}

// GET /api/kuji で呼ばれる関数
router.get('/', (req, res) => {
  res.json({result: get_kuji()})
})

module.exports = router
app.js
var kuji = require('./routes/kuji')

app.use('/api/kuji', kuji)

この時点で,npm start でサーバを起動してブラウザから http://localhost:3000/api/kuji へアクセスすると以下のような JSON のデータが表示されるはずです。

api.png

クライアント側の実装

画面上の「ひく」ボタンをクリックすると,おみくじの結果が画面に表示されるコンポーネントを作成します。

/src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import request from 'superagent'

class Kuji extends React.Component {
  constructor(props) {
    super(props)
    // デフォルトの state を設定
    this.state = { result: '' }
    // api() の中で this にアクセスできるよう bind しておく
    this.api = this.api.bind(this)
  }

  // サーバ側にアクセス(GET /api/kuji)して結果を取得して state に格納する
  api () {
    request.get('api/kuji').end((err, res) => {
      if (err) {
        console.log(err)
        return
      }
      const result = res.body.result
      this.setState({ result: result })
    })        
  }

  render () {
    return (
      <div>
        <p>{this.state.result}</p>
        <button onClick={ e => this.api() }>ひく</button>
     </div>
    )
  }
}

ReactDOM.render(
  <Kuji />,
  document.getElementById('root')
)

この時点で以下のコマンドを実行して

$ npm run build
$ npm run dev

画面上の「ひく」ボタンをクリックすると以下のような画面が表示されると思います。

kuji.png

以上で終わりです。通常の Express, React でアプリを書いたコードがそのままネイティブアプリ化できるのは便利なケースもあるのではないかと思います。

29
23
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
29
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?