LoginSignup
8

More than 5 years have passed since last update.

Electron + Reactの環境作成

Last updated at Posted at 2018-04-26

下準備

下準備
npm init

# なんでもいいけど、エントリーポイントはmain.jsにしておく
entry point: (index.js) main.js
必要なモジュールのインストール
npm install \
electron-reload \
material-ui \
ramda \
react \
react-dom

# electron-reload ・・・ 開発時のオートリロード用
# material-ui ・・・ MaterialUI
# ramda ・・・ 個人的に使いやすいのでRamda.js

npm install -D \
electron \
electron-packager \
webpack \
webpack-cli \
webpack-node-externals \
babel-loader \
html-loader \
html-webpack-plugin \
babel-cli \
babel-preset-env \
babel-preset-react \
babel-preset-stage-1 \
babel-register

# electron-packager ・・・ Electronをパッケージするのに使う
# webpack-node-externals ・・・ node_modulesをwebpackの対象から外す
# babel-preset-stage-1 ・・・ MaterialUIで必要だったはず
# babel-register ・・・ webpack.configだってES6で書きたい

↓.babelrcとwebpack.configはコピペで。

.babelrc
{
    "presets": [
        ["react"],
        ["stage-1"],
        [
            "env",
            {
                "targets": "electron"
            }
        ]
    ]
}
webpack.config.babel.js
import HtmlWebpackPlugin from 'html-webpack-plugin';
import nodeExternals from 'webpack-node-externals';
import webpack from 'webpack';

export default {
    mode: 'development',
    target: 'electron-renderer',  // レンダラープロセスをメインターゲットに
    externals: [nodeExternals()], // node_modulesは依存関係から外すよ
    entry: './src/js/index.js',
    devtool: 'inline-source-map',
    module: {
        rules: [
            {
                test: /\.(html)$/,
                use: {
                    loader: 'html-loader'
                }
            },
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            'babel-preset-react',
                            'babel-preset-env'
                        ]
                    }
                }
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/html/index.html'
        }),
        new webpack.ProvidePlugin({
            $: 'jquery'
        })
    ]
};

ブラウザプロセス

以下をコピペ。

※内容は、
<公式の記載>
+ <electron_reload>
+ <パッケージ後のメニュー>
+ <ロードするhtmlの変更>

main.js
const {app, BrowserWindow, Menu} = require('electron');
require('electron-reload')(__dirname);
const path = require('path');
const url = require('url');

const template = [
    {
        label: 'Edit',
        submenu: [
            {role: 'undo'},
            {role: 'redo'},
            {type: 'separator'},
            {role: 'cut'},
            {role: 'copy'},
            {role: 'paste'},
            {role: 'pasteandmatchstyle'},
            {role: 'delete'},
            {role: 'selectall'}
        ]
    },
    {
        label: 'View',
        submenu: [
            {role: 'reload'},
            {role: 'forcereload'},
            {role: 'toggledevtools'},
            {type: 'separator'},
            {role: 'resetzoom'},
            {role: 'zoomin'},
            {role: 'zoomout'},
            {type: 'separator'},
            {role: 'togglefullscreen'}
        ]
    },
    {
        role: 'window',
        submenu: [
            {role: 'minimize'},
            {role: 'close'}
        ]
    },
    {
        role: 'help',
        submenu: [
            {
                label: 'Learn More',
                click () { require('electron').shell.openExternal('https://electronjs.org'); }
            }
        ]
    }
];

if (process.platform === 'darwin') {
    template.unshift({
        label: app.getName(),
        submenu: [
            {role: 'about'},
            {type: 'separator'},
            {role: 'services', submenu: []},
            {type: 'separator'},
            {role: 'hide'},
            {role: 'hideothers'},
            {role: 'unhide'},
            {type: 'separator'},
            {role: 'quit'}
        ],
    });

    // Edit menu
    template[1].submenu.push(
        {type: 'separator'},
        {
            label: 'Speech',
            submenu: [
                {role: 'startspeaking'},
                {role: 'stopspeaking'}
            ]
        }
    );

    // Window menu
    template[3].submenu = [
        {role: 'close'},
        {role: 'minimize'},
        {role: 'zoom'},
        {type: 'separator'},
        {role: 'front'}
    ];
}

let win;

function createWindow () {
    win = new BrowserWindow({width: 800, height: 600});

    win.loadURL(url.format({
        // ここのhtmlをdistのhtmlにする
        pathname: path.join(__dirname, 'dist/index.html'),
        protocol: 'file:',
        slashes: true
    }));

    //win.webContents.openDevTools();

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

    const menu = Menu.buildFromTemplate(template);
    Menu.setApplicationMenu(menu);
}

app.on('ready', createWindow);

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

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

レンダラープロセス

前準備

mkdir -p ./src/html ./src/js ./src/jsx dist

HTML

index.htmlはReactの入り口だけ作ってあげる。
MaterialUI用にRobotoの設定もここで。

src/html/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
    <style>
      * {font-family: 'Open Sans', sans-serif;}
    </style>
    <title>タイトル</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

JS

ReactDOM.render。

これもほぼコピペでよく、あとはコンポーネントを増やして、追記していけば良い。

MuiThemeProviderをここで記載して、どこでもMaterialUIを使えるようにしておく。

src/js/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';

class App extends Component {
  render() {
    return (
        <MuiThemeProvider>
            hoge
        </MuiThemeProvider>
    );
  }
}

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

Electron起動

$(npm bin)/webpack
$(npm bin)/electron .

一応コンポーネントも増やしてみる

src/jsx/sub.jsx
import React, { Component } from 'react';

export default class Sub extends Component {
    render() {
        return (
            <div>
               Sub
            </div>
        );
    }
}

index.jsでは、Subをimportする。

src/js/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';

import Sub from '../jsx/sub.jsx';

class App extends Component {
  render() {
    return (
        <MuiThemeProvider>
            hoge
            <Sub />
        </MuiThemeProvider>
    );
  }
}

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

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
8