ReactをBabelとWebpackしてElectron

もしかしてElectronでデスクトップアプリを開発したくて、ReactでUIを作りたいと思ってませんか？
この記事を見ているということはそういうことですね。

では、ReactをBabelとWebpackしてElectronしましょう:hugging:

## 準備
これからやることは至ってシンプルです。

**React** （のコード）を **Babel** （でトランスパイル）と **Webpack** （でひとつのjsに変換）して **Electron** （上で動か）します。

正確にはHello, world!を表示させるReactのコードをElectron上で動かすところまでの基礎チュートリアルです。

まずは必要な環境の構築から行っていきましょう。

### Node.jsとnpmのインストール
大半の人が終わっているでしょう。Node.jsに全く触れずにこの記事に来ているあなたはとても勇敢です。
[Node.jsのインストール](https://nodejs.org/ja/download/package-manager/#macos)

```console:console
$ node --version
v14.5.0
```

Node.jsインストールの際には付属でパッケージマネージャの`npm`もインストールされているはずです。
`npm`は最初にアップデートしましょう。

```console:console
$ npm i -g npm
+ npm@6.14.8
updated 1 package in 4.852s
```

完ぺきですね:relieved:

### パッケージ立ち上げと必要パッケージインストール

Node.jsの新規パッケージ立ち上げは`init`サブコマンドです。
パッケージ用のディレクトリを作って、カレントディレクトリをその中に移してからコマンドを打ちます。

```console:console
$ npm init
```
いくつか質問されますが、これらは後で`package.json`で変更が可能なので全てEnterを押して進めましょう。

開発に必要なパッケージを`npm`で導入しましょう。

```console:console
$ npm i -S react react-dom
$ npm i -D electron webpack webpack-cli webpack-loader @babel/core @babel/plugin-transform-react-jsx @babel/plugin-transform-react-jsx-self babel-loader css-loader style-loader
```

### `package.json`の編集

自動生成された`package.json`ファイルを編集します。

今回は

1. `src/main.js`を最初に呼び出されるスクリプトに指定する
2. `scripts`に`build`と`start`を追加

の2点を変更します。
1は`main`の値の書換、2は`scripts`の下に追記しましょう。

以下は2020/10/30時点でのバージョンです。

```json:package.json
{
  "name": "demo",
  "version": "1.0.0",
  "description": "",
  "main": "src/main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "electron ."
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "^17.0.1",
    "react-dom": "^17.0.1"
  },
  "devDependencies": {
    "@babel/core": "^7.12.3",
    "@babel/plugin-transform-react-jsx": "^7.12.1",
    "@babel/plugin-transform-react-jsx-self": "^7.12.1",
    "babel-loader": "^8.1.0",
    "css-loader": "^5.0.0",
    "electron": "^10.1.5",
    "style-loader": "^2.0.0",
    "webpack": "^5.3.2",
    "webpack-cli": "^4.1.0",
    "webpack-loader": "0.0.1"
  }
}

```

準備完了！

## webpackの設定

webpackの設定には`webpack.config.js`ファイルを用います。パッケージのディレクトリ直下にファイルを作り、以下のように編集しましょう。
この設定を簡単に説明すると、

1. `src/app.js`ファイルを基点にインポートされているファイルをまとめる
2. `js`、`jsx`が拡張子のファイルはBabelでトランスポートする`babel-loader`でまとめる
3. `css`が拡張子のファイルは`style-loader`と`css-loader`でまとめる

となっています。

```js:webpack.config.js
module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    path: `${__dirname}/src/dist`,
    filename: 'bundle.js'
  },
  module: {
    rules: [{
      test: /\.js[x]?$/,
      exclude: /node_modules/,
      use: 'babel-loader'
    }, {
      test: /\.css$/,
      use: ['style-loader', {loader: 'css-loader'}],
    }]
  },
  target: 'node',
  resolve: {
    extensions: [".js", ".jsx"]
  },
}
```

## Babelの設定

このままではBabelがReact構文のjsを読みこめません。
そこでBabelがReact構文のjsをトランスパイルできるようにパッケージのディレクトリ直下に`babel.config.js`を設置してプラグインを指定します。

```js:babel.config.js
module.exports = {
  "plugins": ["@babel/transform-react-jsx"]
}
```

## Reactのファイルを設置

事前設定が一通り終わったのでアプリの中身のコーディングに移りましょう。
`src`ディレクトリを作り、`app.js`を以下の内容で設置します。

```jsx:src/app.js
import React from 'react'
import ReactDOM, { render } from 'react-dom'

const App = () => {
  return (
    <div>
      <h1>
        Hello, world!
      </h1>
    </div>
  )
}

render(<App />,document.getElementById('app'))
``` 

## Electronのファイルを設置

Electronのメインプロセスのファイルを`src`ディレクトリ下に以下の内容で設置しましょう。
これは`package.json`で`main`に指定した __最初に呼び出されるスクリプト__ に当たります。
内容はほぼ[公式Quick Start Guide](https://www.electronjs.org/docs/tutorial/quick-start)のままです。
ただし`win.loadFile()`の引数に`'src/index.html'`を与えるよう書き換えましょう。

```js:src/main.js
const { app, BrowserWindow } = require('electron')

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  win.loadFile('src/index.html')
  // win.webContents.openDevTools() // 開発者ツールを非表示にするためコメントアウトしています!

}

app.whenReady().then(createWindow)

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

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
})
```

続いてレンダラープロセスを担う`index.html`ファイルを同様に以下の内容で設置します。

レンダラーのhtmlは最小限の構成ですが、重要なのはReactで使う`id`が`app`の`div`タグと、Webpackされて出力される`src/dist`ディレクトリ下の`bundle.js`を`script`タグで読み込む点です。

```html:src/index.html
<html>
  <head>
    <meta charset="utf-8">
    <title>DemoApp</title>
  </head>
  <body>
    <div id='app'></div>
    <script src='./dist/bundle.js'></script>
  </body>
</html>
```

## ディレクトリ構造おさらい

ここまででのディレクトリ構造は以下のようになります。

```console:tree
demo (package name)
├── src
│   ├── dist
│   │   └── bundle.js
│   ├── app.js
│   ├── index.html
│   └── main.js
├── babel.config.js
├── package-lock.json
├── package.json
└── webpack.config.js
```

（node_modulesディレクトリは記載していません。）

## ReactをBabelとWebpackしてElectron

タイトル回収です。ではやっていきましょう。

__ReactをBabelとWebpackしてElectron__

```console:console
$ npm run build && npm run start
```

![demoapp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233690/72c2b258-c74f-1fae-f968-5e0a05d06bbd.png)

みなさん大好きなHello, world!が出ました、やったね:hugging:

あとは思うがままに、コンポーネントを増やしたりしてアプリのUIをカスタムしましょう！

## 最後にお願い

この記事を書いた私は趣味でしかプログラミングをしたことがない学生です。
このチュートリアルで足りない点はまだ多いと思いますので、__経験豊富な方々は是非ご意見をお寄せください__。
この記事がより良いElectron + Reactのチュートリアル記事になれば、と思っています。