Babel, webpack, flow & eslintを使ってReactの開発環境を構築する

## 目的

Reactとは何ぞやという概念的な記事や実際の書き方を説明した記事は多い。
しかし、Reactでアプリケーション開発を実際にするにあたって具体的にどうすればいいのかという情報は断片的で初心者には全体像が掴めない。

例えば、モジュールバンドラーが必要だと知っている人は、webpackを設定する記事を調べれば分かるが、そもそもReactの開発環境の構築にそれが必要だと知らなければその記事にたどり着くことはできない。
Reactというフレームワークをインストールしさえば動かせるというものではなく、また動いたとしても実際の開発ではflowやeslintを使うことが多い。実際の開発環境は様々なライブラリが協働しているのが常である。

これらの背景を踏まえて、この記事では
**Reactの開発環境を実際に0から構築する過程を通じて、開発環境では何が使われているかという全体像と、
それらの設定はどうすればいいかを経験的に理解すること**
を目的としている。

したがって、全体像の構成要素ごとに詳細な説明はしない。むしろ掘り下げる必要性自体に気づくことを目指している。

全体の流れとしては、

- Node.jsのインストール
- Yarnの使い方
- webpackの設定
- Babelの設定
- Reactのインストール
- eslintの設定
- flowの設定

について順を追って説明する。

## 環境

### 利用したバージョン

```js
> node -v
v8.11.1

> yarn info webpack version
4.7.0

> yarn info eslint version
5.0.0-alpha.2

> yarn info flow-bin version
0.71.0
```

### ファイル構成

```
react-experiment
│  .babelrc
│  .eslintrc
│  .flowconfig
│  .node-version
│  package.json
│  webpack.config.js
│  yarn.lock
└─ src
    ─ index.js
    ─ sub.js
└─ dist
    ─ bundle.js
    ─ index.html
```

## Node.jsのインストールとバージョン指定
### Node.jsとは
- JavaScriptをサーバーサイドで実行するための環境
- パッケージのインストール、ビルド、テストなどの実行環境としても使われる

### nodenv
**nodenvとは？**
nodenvとはNode.jsのバージョンを管理するためのツール。
指定のバーションをインストールしたり、利用するバージョンを切り替えたりできる。

他にはndenvやnodebrewなどがあるが、nodenvは.node-versionファイルを参照して、そこに記載されたバージョンに自動で切り替えてくれるので便利だったためこちらを利用。

実際のインストール方法はこちらの記事を参考にした。
[Nodenv環境を用意する - Qiita](https://qiita.com/YuukiMiyoshi/items/080b6cde332d8d4e06f3)

**nodenvでnode.jsをインストールする**

```js
// すでにnodebrewを使っている場合はパスを外す（でないとそちら優先されることがある）
// 今回は8.11.1を使用するためにインストール
nodenv install 8.11.1
nodenv rehash
// ローカル環境で利用するバージョンを指定する
nodenv local 8.11.1
// バージョンの確認
nodenv version
=> 8.11.1
node -v
=> 8.11.1
```

## Yarn
### Yarnとは？
Yarnとはパッケージマネジャーの一つ。
概要を理解するために押さえておくべきなのは、package.jsonとyarn.lockの二つ。[依存関係とバージョン | Yarn](https://yarnpkg.com/ja/docs/dependencies)

- package.json: インストールしたパッケージのバージョンなどが管理されている
- yarn.lock: 直接編集することはないが、パッケージ自体が依存しているパッケージ（サブパッケージ）も含めて管理されている

### 基本的なコマンド

まずプロジェクトの新規作成をする。

```js
yarn init
```

主に使うのは`yarn add`コマンド。[yarn add | Yarn](https://yarnpkg.com/lang/ja/docs/cli/add/)

Yarnはパッケージへの依存関係をグループ化して管理する。
主に利用されるのは、`dependencies`と`devDependencies`の二つ。
詳しくは[こちらのページ](https://yarnpkg.com/ja/docs/dependency-types)を参照されたし。

`yarn add <pachage...>`で`dependencies`としてインストールされる。
> これれは通常の依存関係か、もしくはコードの実行時に必要なもの（例えば React または ImmutableJS など）です。

`yarn add <pachage...> [--dev/-D]`で`devDependencies`としてインストールされる。
> これらは開発用の依存関係です。開発ワークフローのどこかで必要で、コードの実行時には必要のない依存関係です（例：Babel もしくは Flow）。

### versionの指定方法
package.jsonに記載されるパッケージのバージョン。
[Versions of dependencies | Yarn](https://yarnpkg.com/lang/en/docs/dependency-versions/)
Caret Ranges (`^`) はゼロ以外の最初の桁の数字を上げない限りのバージョンアップを許す。
`^2.3.1` => `>=2.3.1 < 3.0.0`
`^0.5.1` => `>=0.5.1 < 0.6.0`
`^0.0.7` => `>=0.0.7 < 0.0.8`

## webpack
### webpackとは？
JavaScriptやHTML, CSSはブラウザ上で描画されるために処理、実行される。
この辺りの仕組みの概要を知りたければこちらの本の前半の章を読むと分かりやすい。
[Webフロントエンド ハイパフォーマンス チューニング | 久保田 光則 | 工学 | Kindleストア | Amazon](https://www.amazon.co.jp/dp/B0728K5JZV/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

そのためには、サーバにあるJavaScriptなどのファイルをロードする必要がある。
しかし、これらのファイルは通常複数あり、全てファイルを読み込み、処理をするとユーザは延々と待ち続けることになる。そのために、それらのファイル（モジュール）をまとめて一つにし、ブラウザ（JavaScriptエンジンなど）が実行しやすいようにビルドしたファイルを作る必要がある。それを実現するのがモジュールバンドラー。

そしてwebpackはアクセスしたページで利用されているファイルを読み込み一つのファイルにすることができる。

> しかし、昨今ではフロントエンドの担う役割は年々増大しており、それに伴ってフロントエンドの占めるコードの割合も増大しているので、それを1つのファイルに落とし込むとなると、ファイルサイズはかなりのものになります。(中略) そのファイルを初回アクセス時にユーザーに読み込ませるとなると、無関係のコードも含まれる1つのファイルの読み込みを、ユーザーは延々と待たされることになります。(中略) 理想はアクセスしたページに依存関係のあるコードだけが含まれるファイルが読み込まれ、初期ローディングが必要最小限に抑えられることでしょう。これらの問題を解決するために既存のモジュール・バンドラーを拡張することも試みられたそうですが、目的を果たすことができずwebpackを開発するに至ったことが記されています。
[これから始めるwebpack - webpackの基礎知識 | CodeGrid](https://app.codegrid.net/entry/2017-webpack-basics-1)

### webpackの設定をする
主にこの記事を参考にした。[サルでもわかるwebpack 4.5入門 - 古い情報に騙されずに学ぼう - ICS MEDIA](https://ics.media/entry/12140)

**webpackをインストールする**

```js
yarn add -D webpack webpack-cli
```

**webpack.config.jsを設定する**
複数のJavaScriptファイルを集約したJavaScriptファイルをエントリーポイントという。

```js
module.exports = {
  // エントリーポイント
  entry: `./src/index.js`,
 
  // ビルドされた実行ファイルの出力設定
  output: {
    //  出力ファイルのディレクトリ名
    path: `${__dirname}/dist`,
    // 出力ファイル名
    filename: 'bundle.js'
  },
};
```

### webpackでバンドルしてみる
**JavaScriptのバンドルファイルを作る**

```js
// ./src/index.js
import { hello } from './sub';
hello();

// ./src/sub.js
export function hello() {
  alert('Hello World');
}
```

実際にビルドされたファイル。
設定でモードを選ぶことでソースマップ（どこのソースコードから作られたか）などの情報も付与して出力することができる。
デフォルトだと本番用に圧縮された状態で出力される。

実際にビルドしてみる。

```js
> yarn run webpack
Hash: 302f700e2ea67728f905
Version: webpack 4.7.0
Time: 259ms
Built at: 2018-05-05 17:19:47
    Asset       Size  Chunks             Chunk Names
bundle.js  587 bytes       0  [emitted]  main
Entrypoint main = bundle.js
[0] ./src/index.js + 1 modules 91 bytes {0} [built]
    | ./src/index.js 40 bytes [built]
    | ./src/sub.js 51 bytes [built]
```

`./dist/bundle.js`に出力される。

```js
// ./dist/bundle.js 
!function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r,t){"use strict";t.r(r),alert("Hello World")}]);
```

## Babel
### Babelとは？
ECMAScriptのトランスパイラ。flowTypeやJSX、ECMAScript2015 (ES6) などのソースコードをサポートしていないブラウザが存在するので、実行できるように取り除いたり、それ以前の書き方に変換したりすることができる。
[こちら](http://babeljs.io/repl/#?babili=false&evaluate=true&lineWrap=false&presets=latest%2Creact%2Cstage-2&experimental=false&loose=false&spec=false&code=%5B1%2C2%2C3%5D.map(n%20%3D%3E%20n%20%2B%201)%3B&playground=true)のページで試すことができる。
そして、これらが変換された後にwebpackがバンドルする。

flowtype付きのES2015の記法を使って書いたもの。

```js
// @flow

const inc = (x: number):number => { x + 1 };

const newArray: Array<numebr> = [...[1, 2, 3], 4, 5];
```

それらがES2016以前の書き方に変換される。

```js
"use strict";

var inc = function inc(x) {
  x + 1;
};

var newArray = [1, 2, 3].concat([4, 5]);
```

### Babelを使う
主にこの記事を参考にした。[最新版で学ぶwebpack 4入門 - BabelでES2017環境の構築(React, Vue, Three.js, jQueryのサンプル付き) - ICS MEDIA](https://ics.media/entry/16028)

**インストールする**

```js
yarn add -D babel-core babel-loader babel-preset-env babel-preset-react
```

**webpack.config.jsを設定する**

```js
// webpack.config.js
module.exports = {
  ...
  
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            // Babelを利用する
            loader: 'babel-loader',
            options: {
              presets: [
                ['env', { 'modules': false }]
              ]
            }
          }
        ]
      }
    ]
  }
};
```

## React
**reactをインストールする**

```js
yarn add react react-dom
```

**webpackの設定をする**

```js
// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            // Babel のオプションを指定する
            options: {
              presets: [
                ['env', { 'modules': false }],
                // React の JSX を解釈
                'react'
              ]
            }
          }
        ],
        // node_modules は除外する
        exclude: /node_modules/,
      }
    ]
  }
};
```

**babelの設定をする**

```js
// .babelrc
{
  "presets": ["react"]
}
```

**webpack-dev-serverを使う**
まずインストールする。

```js
yarn add -D webpack-dev-server
```

webpackで起動させるサーバの設定を行う。

```js
// webpack.config.js
module.exports = {
  entry: `./src/index.js`,

  ...

  devServer: {
    port: 3000,
    contentBase: 'dist'
  },

  module: {
    ...
  }
};
```

`yarn run webpack-dev-server`で`http://localhost:3000/`にアクセスすると表示されるようになる。


**実際に書いてみる**

```jsx

// ./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import HelloMessage from './sub';

class App extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello React!</h1>
        <HelloMessage
          message="with Babel & webpack"
        />
      </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('react-root'));

// ./src/sub.js
const HelloMessage = (props) => {
  <h3>{props.message}</h3>
};
export default HelloMessage;

// ./dist/index.html
<!doctype html>
<html lang="en">
  <body>
    <div id="react-root"></div>
    <script src="/bundle.js"></script>
  </body>
</html>
```


表示される！

![スクリーンショット 2018-05-05 17.57.46.png](https://qiita-image-store.s3.amazonaws.com/0/68018/bc67ccd2-ba85-ab28-1f5a-4723b4d4ee0d.png)


## eslint
eslintとは、JavaScritpやJSXの構文チェッカーのこと。
この記事などを見て設定をした。
[ESLint 最初の一歩 - Qiita](https://qiita.com/mysticatea/items/f523dab04a25f617c87d)

インストールする。

```js
yarn add -D eslint eslint-plugin-react
```

.eslintrcファイルでチェック時のルールを設定することができる。

```js
// .eslintrc
{
  "parser": "babel-eslint",
  "plugins": [
    "flowtype",
    "react"
  ],
  "settings": {
    "flowtype": {
      "onlyFilesWithFlowAnnotation": true
    }
  },
  "parserOptions": {
    "sourceType": "module",
      "ecmaFeatures": {
      "jsx": true
    }
  },
  "env": {
    "browser": true,
      "node": true
  },
  "rules": {
    "quotes": [2, "single"],
      "strict": [2, "never"],
      "react/jsx-uses-react": 2,
      "react/jsx-uses-vars": 2,
      "react/react-in-jsx-scope": 2
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:flowtype/recommended"
  ]
}
```

## flow

**インストールと実行**
JavaScritpを型定義するためのもの。こちらのサイトに従ってインストールできる。[Installation | Flow](https://flow.org/en/docs/install/)

```js
yarn add -D flow-bin
yarn run flow init
yarn run flow
```

**基本設定**
`.flowconfig`でバージョン指定や、チェック対象のファイルを指定できる。

```js
// node_module以下はチェック対象外にする
[ignore]
.*/node_modules/.*
```

**トランスパイル時に型定義されたコードを取り除く**
[balel-preset-react](https://babeljs.io/docs/en/babel-preset-react)にflowの型定義を取り除くプラグインが含まれている。


babelrcの設定をする。

```js
// .babelrc
{
  "presets": ["react"],
}
```

**eslintに型定義されたコードを通す**
この記事を参考にした。[flowの始め方 - Qiita](https://qiita.com/kiida/items/ea758e4d80053663e12a)

eslintと連携するには[babel-eslint](https://github.com/babel/babel-eslint)と[eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype)をインストールする必要がある。
そうでないとeslintで引っかかる。

```js
yarn add -D eslint-plugin-flowtype babel-eslint
```

.eslintrc.jsに以下を追加する。

```js
//.eslintrc.js
{
  parser: 'babel-eslint',
  extends: ['plugin:flowtype/recommended'],
  plugins: ['flowtype'],
  settings: {
    flowtype: {
      onlyFilesWithFlowAnnotation: true,
    },
  },
  ...
}
```

flowを使って型定義をする。

```jsx
// @flow

import React from 'react';

type Props = {
  message: string
};

const HelloMessage = (props: Props) => {
  return <h3>{props.message}</h3>;
};
export default HelloMessage;
```

## 参考
- [Nodenv環境を用意する - Qiita](https://qiita.com/YuukiMiyoshi/items/080b6cde332d8d4e06f3)
- [サルでもわかるwebpack 4.5入門 - 古い情報に騙されずに学ぼう - ICS MEDIA](https://ics.media/entry/12140)
- [はじめてのWebpack - Qiita](https://qiita.com/cheez921/items/266b1b4534b6111a9686)
- [webpack 4 入門 - Qiita](https://qiita.com/soarflat/items/28bf799f7e0335b68186)
- [これから始めるwebpack - webpackの基礎知識 | CodeGrid](https://app.codegrid.net/entry/2017-webpack-basics-1)
- [最新版で学ぶwebpack 4入門 - BabelでES2017環境の構築(React, Vue, Three.js, jQueryのサンプル付き) - ICS MEDIA](https://ics.media/entry/16028)
- [Yarnを利用して、Reactをwebpack と babel-loaderでビルドする方法 - Qiita](https://qiita.com/SatouRyou/items/6169800993587f4e6018)
- [ESLint 最初の一歩 - Qiita](https://qiita.com/mysticatea/items/f523dab04a25f617c87d)
- [flowの始め方 - Qiita](https://qiita.com/kiida/items/ea758e4d80053663e12a)