electron環境にwebpackとelectron-connectを足す

electron, react, babel, webpack, gulpの環境にした。

今までのものからwebpackを追加した。

その理由は、jsxファイルを分割して、他ファイルからrequireするときに、正しくできないため。 requireが無いと言われたり、パスが異なると言われたりする。 パスが違う原因は、おそらくパス指定がhtmlファイル基準になるためだと思う。 gulp+babelでのトランスパイルは、ファイルはまとまらないようなので、これではまずい。

この問題を解決するために、webpackの導入を行った。

さらに、main.jsなどのメインプロセス側ファイルの変更のホットリロードに対応するため、electron-connectを導入し、gulpfileからelectronを操作できるようにした。

導入とwebpack関連のセットアップ

まずは、パッケージのインストール(追加分のみ、かつelectron-connectは後回し)。

npm install -D babel-loader webpack webpack-stream

続いて、webpackの設定。resolvemoduleの中身が大事:

// webpack.config.js
module.exports = {
    // モード値を production に設定すると最適化された状態で、
    // development に設定するとソースマップ有効でJSファイルが出力される
    mode: "development",

    // メインのJS
    entry: "./renderer/index.jsx",
    // 出力ファイル
    output: {
        filename: "bundle.js"
    },
    resolve: {
        extensions: ['.jsx']  // デフォルトは、jsxファイルを探してくれない。
    },
    module: {
        rules: [{
            test: /\.jsx$/, // 拡張子がjsxで
            exclude: /node_modules/, // node_modulesフォルダ配下でなければ
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env', '@babel/preset-react']
                }
            }
        }]
    }
}

次は、gulpのタスク定義:

// gulpfile.js
const gulp = require('gulp');
const babel = require('gulp-babel');
const webpackStream = require('webpack-stream');
const webpack = require('webpack');

function transpileViaWebpack() {
    // webpackStreamの第2引数にwebpackを渡す
    return webpackStream(webpackConfig, webpack)
        .pipe(gulp.dest("dist"));
}
exports.default = transpileViaWebpack;

また、watchを変更すればwebpackにしてもホットリロードができる。githubのソースでは、そうしている。しかし、electron-connectを利用している点で異なる。(後述)

Reactのコンポーネント

rendererプロセス用のファイル構成は下のようになる。

renderer/
├── components
│   ├── app.jsx
│   └── person.jsx
├── index.html
└── index.jsx

分割したので、index.jsxは以下のようになる。

// index.jsx
const { App } = require('./components/app');

document.addEventListener("DOMContentLoaded", () => {
    ReactDom.render(<App />, document.getElementById('app'));
})

分割先の./components/app.jsxは以下に。

// components/app.jsx
const { Person } = require('./person');
class App extends React.Component {
    render() {
        return (
            <div>
                <h1>hello electron+react+webpack world!</h1>
                <Person food="sushi" />
            </div>
        );
    }
}

module.exports = {
    App: App,
};

さらに分割した./components/person.jsxは以下に。

// components/person.jsx
class Person extends React.Component {
    render() {
        return (
            <p>i want to eat {this.props.food}!</p>
        )
    }
}

module.exports = {
    Person: Person,
};

htmlでの読み込み元の変更

gulpfile.jsで、トランスパイル後の出力ディレクトリ先をdistに変更したので、htmlファイルでの読み込み元もそれに合わせて変更する。

<!-- index.html -->
...
<!-- 以前の出力先 -->
<!-- <script src="../build/index.js"></script> -->
<!-- 現在の出力先 -->
<script src="../dist/bundle.js"></script>

gulpfileからelectronの起動・ホットリロード

今までの内容は、起動時のみ事前のトランスパイル(npx gulp)が必要だった。 これは、若干面倒なので、npm start時にトランスパイルするようにする。 これには、gulp上でelectronの実行ができればいいと考えた。

そこで、electron-connectというツールが使えることがわかった。インストール:

npm install -D electron-connect

メインプロセス側でclientを生成する。html内に埋め込む形にした。配布時には除去できるらしい。(未確認)

<body>
    ...
    <!-- build:remove -->
    <!-- Connect to server process -->
    <script>electronconnect.client.create()</script>
    <!-- end:build -->
</body>

electronconnectなのは、preload.jsでグローバル変数にセットしてあるから。

// preload.js
...
process.once('loaded', () => {
    ...
    global.electronconnect = require('electron-connect');
})

gulpfile.jsに追記。watchcallback()しないと、一度の変更時しか関数が実行されない。(なぜかelectron.restartはしっかり動いたが。):

// gulpfile.js
const electron = require('electron-connect').server.create();
...
function runElectron() {
    electron.start();
}

function watch() {
    gulp.watch(paths.main.src, electron.restart);
    gulp.watch(paths.renderer.srcjsx, (callback) => {
        transpileViaWebpack();
        electron.reload();
        callback();
    });
    gulp.watch(paths.renderer.srchtml, (callback) => {
        electron.reload();
        callback();
    });
}
exports.run = gulp.series(transpileViaWebpack, gulp.parallel(runElectron, watch));

これで、npx gulp runすればOK。package.jsonを変更して、npm startでこれを実行するようにする。

// package.json
"scripts": {
    "start": "gulp run",
    ...
},

electron-connectはメインプロセスとレンダラープロセスの両方をリロードできるので、今まで使っていた、electron-reloadは不要になる。よって、アンインストールしておく。

npm uninstall -D electron-reload

後書き

gulpwatchcallback()が必要なことに気がつくのに時間がかかった。

自分で環境を構築していくと、いろいろわかってきて楽しい。あまり時間はかけたくないが。

配布のためのパッケージングについても、そのうちまとめておきたい。

最後に現在のpackage.jsonの依存関係を載せておく。:

// package.json
...
"devDependencies": {
    "@babel/core": "^7.4.5",
    "@babel/preset-env": "^7.4.5",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.6",
    "electron-connect": "^0.6.3",
    "electron-mocha": "^8.0.3",
    "gulp": "^4.0.2",
    "gulp-babel": "^8.0.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "webpack": "^4.35.2",
    "webpack-stream": "^5.2.1"
},
"dependencies": {
    "electron": "^5.0.6"
}

参考

全体のソース

タイトルとURLをコピーしました