webpackとBabelの基本を理解する(4) ―React編―

独学の内容をまとめたものです。誤りがございましたら、ご連絡いただけると幸いです。

**リンク**
1. [webpackとBabelの基本を理解する(1) ―webpack編―](https://qiita.com/koedamon/items/3e64612d22f3473f36a4)
2. [webpackとBabelの基本を理解する(2) ―Babel編―](https://qiita.com/koedamon/items/92c986456e4b9e845acd)
3. [webpackとBabelの基本を理解する(3) ―webpackとBabel編―](https://qiita.com/koedamon/items/6cf2201be78c3d79516d)
4. webpackとBabelの基本を理解する(4) ―React編―（本記事）
5. [webpackとBabelの基本を理解する(5) ―Sass編―](https://qiita.com/koedamon/items/a5be9456e0591db4d1f4)

# 概要
## この記事の概要

- 目的
  - フロントエンドの環境構築に利用されるツールへの理解を深める
- 本記事のゴール
  - ReactのJSX構文をコンパイルする方法を知る
- 対象者
  - WEBフロント担当者
  - HTML,CSS,JavaScript(es2015含む)の基本的な構文を理解している人
  - npmの利用方法を理解している人
- 環境・バージョン
  - Windows10
  - Node.js(推奨版) 10.15.01
  - npm 6.4.1
  - webpack 4.29.6
  - webpack-cli 3.2.3
  - babel-core 7.3.4
  - babel-cli 7.2.3 
  - babel-loader 8.0.5
  - preset-env 7.3.4

## React
Reactそのものの説明は割愛させていただきます。。。

[React](https://reactjs.org/)
[React - Wikipedia](https://ja.wikipedia.org/wiki/React)

Reactは、es2015以降の構文の利用が推奨されていることに加え、JSXというそのままではブラウザで動作しない構文を取り入れています。
また、コンポーネントを組み合わせてアプリを開発するという考え方なので、ESモジュールの利用も必須です。
つまり、Babelとwebpackの利用が前提となってきます。

**Create React App**
Reactは事前準備が大変だからと、公式が用意してくれた環境構築用ツールです。筆者がちまちま説明してきた環境構築が、コマンド一つで完了します。
webpackやBabelより先に、Reactの勉強に取り組みたい方はこちらをご利用ください。
[Create React App](https://facebook.github.io/create-react-app/)

# ReactのPreset
## インストール
BabelでReactのコードを変換するには、専用のPresetが必要です。

```console
$ npm install @babel/preset-react --save-dev


# もちろんReact本体も必要なのでまとめて落としたい場合はこちら
$ npm install react react-dom @babel/preset-react --save-dev
```

## 設定
あくまで変換を行うのはBabelですから、基本的に`babel-loader`のオプションに追加します。webpack.config.jsは以下のようになります。

```js
module.exports = {
  /* 略 */
  module: {
    rules: [
      {
        test: /\.js[x]?$/,  // .jsxも対象に含む
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              '@babel/preset-env',
              '@babel/preset-react' //ReactのPresetを追加
            ],
            plugins: ['@babel/plugin-syntax-jsx'] //JSXパース用
          }
        }
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json']  // .jsxも省略可能対象にする
  }
};
```

**rules.test**
babel-loaderの対象ファイルに`.jsx`が含まれるよう、正規表現で指定しています。

### Presetの追加
`options.presets`にReactのPresetを追加します。後ろに指定したものから実行されるので、`preset-react`→`preset-env`の順で変換されるようにします。

[@babel/preset-react](https://babeljs.io/docs/en/babel-preset-react)
### Pluginの追加
JSXパース用のプラグインを指定します。`options.plugins`に、配列でプラグイン名を記述します。

**PresetとPlugin**
最初、PresetとPluginの違いがいまいち理解できていなかったのですが、PluginのコレクションがPresetだそうです。'@babel/plugin-syntax-jsx'は'@babel/preset-react'に含まれます。
最初から有効になっているプラグインのみ利用する場合は、Presetの指定のみで済みます。オプションのプラグインを利用したい場合は、Pluginで指定します。Presetに含まれていないプラグインを独自に追加することも可能です。
`@babel/preset-env`では、具体的に個別のPlugin名をこちらが指定するのではなく、利用したい環境情報を伝えると必要なものを選んでくれるという方法をとっています。
実行順序は、Plugin→Presetです。また、Plugin内での順序は最初から（Presetとは逆）です。

[Plugins | Babel](https://babeljs.io/docs/en/plugins)
[Difference between plugins and presets in .babelrc | stackoverflow](https://stackoverflow.com/questions/45943889/difference-between-plugins-and-presets-in-babelrc)

### resolve.extensions
import文で拡張子を省略するには、予め`resolve.extensions`登録しておく必要があります。初期設定では`['.wasm', '.mjs', '.js', '.json']`です。
`.jsx`も対象にしたい場合は追加する必要があります。この際、設定値は上書きされることに注意してください。`['.jsx']`にしてしまうとJSXファイルだけが対象になってしまいます。


# Reactのソースをコンパイルする
## フォルダとファイルの準備
下記フォルダとファイルを準備します。

```
sample/
  ├ src/
  │   ├ component/
  │   │    └ panel.jsx
  │   └ index.js 
  ├ dist/
  │   ├ js/
  │   └ index.html
  └ webpack.config.js
```

テキストボックスに文字を入力してボタンを押すと、すぐ下のリストに追加されるだけの挙動です。

```jsx
/**
 * /src/component/panel.jsx
 */
import React from 'react';

export default class Panel extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: '',
      list: []
    };
  }
  handleSubmit(e) {
    e.preventDefault();

    const input = '',
      list = this.state.list.slice();

    list.unshift(this.state.input);

    this.setState({
      input,
      list
    });
  }
  render() {
    return (
      <form>
        <input
          type="text"
          value={this.state.input}
          onChange={e => this.setState({ input: e.target.value })}
        />
        <button
          type="submit"
          onClick={this.handleSubmit.bind(this)}
        >
          add!
        </button>
        <ul>
          {this.state.list.map((v, i) => (
            <li key={i}>
              {v}
            </li>
          ))}
        </ul>
      </form>
    );
  }
}


/**
 * /src/index.js
 */
import React from 'react';
import ReactDOM from 'react-dom';
import Panel from './component/panel';

ReactDOM.render(
  <Panel />,
  document.getElementById('app')
);
```

```html
<!-- /dist/index.html -->
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Test</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="./js/sample.js"></script>
  </body>
</html>
```

## 実行
**Charenge!**
webpack.config.jsとpackage.jsonで、以下の設定を自分でやってみよう！（手抜き）

- `/src/index.js`のコンパイル結果が`/dist/js/sample.js`に出力されるようにしよう！
- ブラウザ上で、コンパイル前のソースも確認できるようにしよう！
- ソースコードを編集すると、自動でコンパイルされるようにしよう！
インストールしたパッケージのソースは対象外にすることを忘れずにね！
- npm scripts からバンドルを実行しよう！

それでは、IE11で実行結果を確認してみましょう。

![webpack04.png](https://qiita-image-store.s3.amazonaws.com/0/362737/cf40d427-bc96-ed3c-4624-05fb4d336c9a.png)

無事に動くということは、JSXがJavaScriptに変換され、es2015以降の構文がes5に変換されたということですね。

# 参考情報
[Create React Appで環境構築 – React入門](https://www.to-r.net/media/react-tutorial12/)