React 環境設定(ほぼwebpackの設定)[React 16, Webpack4, babel]

普段はReactNativeやRailsのコードを書いていますが、サーバーサイドのNodeは扱っていないため、勉強のため構築した環境のメモを記載しておきます。
主に`webpack`の設定を中心に、調べた限り書いていきます。また、今回のは最低限の設定です。`HMR`の設定や`Lint`の設定などはありませんのでご了承ください。その辺りはまたの機会で記事にしようと思います。  

### 環境

`node`のバージョンは以下の通り。

```
$ node -v
8.11.2
$ yarn -v
1.7.0
```

### 作業フォルダの作成

```
$ mkdir project-dir
$ cd project-dir
$ yarn init
```

これで作業フォルダとその直下に`package.json`が作成されます。  

### パッケージインストール

```
$ yarn add --dev babel-core babel-loader 
$ yarn add --dev webpack webpack-cli
$ yarn add --dev react react-dom
$ yarn add --dev babel-plugin-transform-react-jsx babel-preset-env babel-preset-react
```

各パッケージのバージョンは次のようになりました。  

```package.json
{
  ...

  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.4",
    "babel-plugin-transform-react-jsx": "^6.24.1",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "react": "^16.4.0",
    "react-dom": "^16.4.0",
    "webpack": "^4.10.2",
    "webpack-cli": "^3.0.1"
  }
}
```


### 動作確認用ファイルの作成

動作確認用にファイル作っていきます。ファイル構成は以下の通り。

```
/----- index.html
  |--- index.js
  |--- App.js
  |--- package.json
  |--- webpack.config.js
  |--- yarn.lock
  -- dist
       |-- ...
```

まずは元となる`index.html`を作業フォルダ直下に作成します。  

```index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <div id="root"></div>
    <script type="text/javascript" src="./dist/bundle.js"></script>
  </body>
</html>
```

`webpack`でビルドされたファイルを`./dist/bundle.js`に出力するので、そのJSファイルを読み込んでいます。  

次に`index.js`と「Hello, world!」を表示する`React`コンポーネントを作成します。  

```index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

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

import React from 'react'

export default class App extends React.Component {
  render () {
    return <h1>Hello, world!</h1>
  }
}
```

### Webpackの設定

`webpack.config.js`を作成し、`Webpack`の設定をしていきます。  

```webbpack.config.js

module.exports = {
  devtool: 'inline-source-map',
  mode: 'development',
  entry: './index.js',
  output: {
    filename: 'bundle.js',
  },
  module: {
    rules: [{
      test: /\.js?$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['env', 'react'],
        },
      },
    }]
  }
}
```

`package.json`にスクリプトを用意しておきます。これで`yarn webpack`をターミナルから、たたけばビルドが行われます。  

```package.json

{
  ...
  "license": "MIT",
  "scripts": {
    "webpack": "webpack --mode development"
  },
  "devDependencies": {
    ...
  }
}
```

次を実行し、`index.html`をブラウザから開くと「Hello, world!」が表示されていれば成功です。  

```
$ yarn webpack
```

### Webpackの設定について

それでは、`webpack.config.js`の各プロパティの説明をしていきます。  

#### mode

ビルド時に指定した環境にあった最適化が行われます。

`mode`オプションを設定せずに、以下のようにコマンドラインからの指定もできます。  

    $ webpack --mode=production

設定値は`development`,`production`,`none`の３つです。設定した場合の機能は次の内容になります。有効となるプラグインですが、まだ殆ど調べていないので名前だけ列挙しておきます。  

- `development`
  - `process.env.NODE_ENV`の値が`development`になる
  - 以下が有効になる  
      - `NamedChunksPlugin`
      - `NamedModulesPlugin` : `HMR`時に、モジュールを相対パスで参照できる
- `production`
  - `process.env.NODE_ENV`の値が`production`になる
  - 以下が有効になる
      - `FlagDependencyUsagePlugin`
      - `FlagIncludedChunksPlugin`
      - `ModuleConcatenationPlugin`
      - `NoEmitOnErrorsPlugin`
      - `OccurrenceOrderPlugin`
      - `SideEffectsFlagPlugin`
      - `UglifyJsPlugin`

#### entry

ビルドの起点となる箇所の設定ができます。関連のモジュールやライブラリとの依存関係を判別するため必要となるそうです。設定しない場合は、`./srv/index.js`が設定値となります。  

書き方は次の形式になります。  

```js
const config = {
  entry: './path/entry-file.js'
};
```

この書き方は以下の簡略です。  

```js
const config = {
    entry: {
    main: './path/entry-file.js'
  }
};
```

`key: value`の形式で設定でき、複数指定してページ毎やモジュール単位などで依存関係を分けることもできます。  

#### output

ビルドしたファイルの配置先を設定できます。設定しない場合は`./dist/main.js`になります。  

以下の2つを設定できます。  

- `filename` : 出力ファイルの名前。
- `path` : 出力するディレクトリの絶対パス。`__dirname + /dist`みたいに設定。

#### module

`Loader`などのモジュールを設定できます。`rules`の中に`Loader`の設定を用意する形になります。
今回は、`babel-loader`を利用するため設定しています。  

`Rule`の設定は次の通りです。  

- `test` : 変換する対象ファイルを正規表現で設定できる。
- `exclude` : 変換対象の除外とするファイルを文字列で設定できる。
- `use` : 利用する`Loader`やその詳細を設定できる。
  - `loader` : `Loader`のモジュール名。
  - `options` : `Loader`のオプション設定。今回は、`babel-preset-env`(ES6記法)、`babel-preset-react`(`React`の記法)を利用する設定をしています。

#### devtool

`source map`の生成を設定できます。`source map`を使用するとデバッグが楽になります。  

- `inline-source-map` : data URLで`source map`をまとめます。

`source map`を設定しないままビルドして、ブラウザの開発ツールでコードを確認してみると、

<img width="1237" alt="Screen Shot 2018-06-10 at 23.31.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/262170/183ca699-c4d2-9754-2091-b8c6ae104fb9.png">

のようになって、元のコードから変わってしまっています。

次に、`devtool: 'inline-source-map'`を設定してビルドしてみると、`dist`配下に`bundle.js.map`が作成されている。先程と同じ様に開発ツールから確認してみると、

<img width="1237" alt="Screen Shot 2018-06-10 at 23.32.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/262170/a0e6b001-0765-5d2b-a7df-4c35c0b58e4b.png">

このように、元のコードのままになっているので、デバッグでの確認がしやすくなります。  

### 参考文献

- [webpack4でReact16のSSR（サーバサイドレンダリング）をする](https://qiita.com/teradonburi/items/32e8339ce93273219661)
- [webpack公式ドキュメント](https://webpack.js.org/concepts)